Java5,Java7实现的<线程池>综述
系统启动一个新线程的成本是比较高的,因为它涉及和操作系统交互.在这种情况下,线程池可以很好的提升性能.尤其是程序中需要创建大量生存周期很短暂的线程时,更应该考虑线程池.
运行机制:与数据库连接池类似的是,线程池在系统启动时就创建大量空闲的线程,程序将一个Runnable或者Callable对象传给线程池,线程池就启动一个线程来执行他们的run或者call方法.当方法结束后,该线程不会死亡.而是再次返回线程池中成为空闲状态,等待下一个runnable/callable的run/call方法.
优点:使用线程池可以有效的控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能急剧下降,导致JVM崩溃.而线程池的最大线程数参数可以控制系统中并发线程数不超过此数.
Java 5实现的线程池
从Java5开始,Java内建支持线程池.Java5新增了一个Executors工厂类来产生线程池.如下
- newCachedTheradPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将被缓存在线程池中.
- newFixedTheradPool(int threadNum):创建一个可重用,有固定线程数的线程池.
- newSingleThreadExecutor():等同上面方法传入的参数为1.
- newScheduledThreadPool(int num):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务.num是指池中所保存的线程数,即使线程是空闲的也被保存在线程池内.
- newSingleThreadScheduledExecutor():创建只有一个线程的线程池,它可以在指定延迟后执行线程任务.
后2个方法返回都是一个ScheduledExecutorService对象,它是ExecutorService的子类,可以指定延迟后执行线程任务.
ExecutorService有三个方法:
- Future<?> submit(Runnable task):将一个Runnable对象提交给指定的线程池.Future<?>是代表Runnable的返回值,但run()方法没有返回值,所以是最后返回null.所以可以根据Future的isDone(),isCancelled()方法来获取Runnable对象的执行状态.
- <T>Future<T> submit(Runnable task,T result):同上,result将显式指定线程执行结束后的返回值,所以Future对象将在run方法执行结束后返回result.
- <T>Future<T> submit(Callable<T> task):将一个Callable对象提交给指定的线程池,其中Future表示Callable对象里call()方法的返回值.
- ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit):指定callable将在delay延迟后执行.
- ScheduledFuture<?> schedule(Runnable runnable,long delay,TimeUnit unit):指定runnable任务将在delay延迟后执行.
- ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable,long initialDelay,long period,TimeUnit unit):指定runnable任务在delay延迟后执行,而且以设定频率重复执行,也就是说,在initialDelay后开始执行,一次在initialDelay+period,initialDelay+2*period...处重复执行,以此类推.
- ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable,long initialDelay,long delay,TimeUnit unit):创建并执行一个在给定初始延迟后首次启用的定期操作,随后在每一次执行终止和下一次执行开始之间都存在给定的延迟.如果任务在某一次执行的时候遇到异常,就取消后续执行,否则,只能通过程序来显式取消或终止任务.
l例子:
package org.credo.thread.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class java5pool { public static void main(String[] args) { //创建一个具有固定线程数的线程池 ExecutorService pool=Executors.newFixedThreadPool(6); //向线程池中提交两个线程 pool.submit(new MyThread()); pool.submit(new MyThread()); //关闭线程池 pool.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"的i值为:"+i); } } }
Java 7实现的线程池ForkJoinPool
ForkJoinPool
Java7提供它用来支持多核CPU的性能优势.将一个任务拆分成多个"小任务"并行计算,再把多个"小任务"的结果合并成总的计算结果.ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池.
它提供了如下两个常用的构造器.
- ForkJoinPool(int num):创建一个保护num个并行线程的ForkJoinPool.
- ForkJoinPool():以Runtime.availableProcessors()方法的返回值作为"num参数"来创建ForkJoinPool.
ForkJoinTask代表一个可以并行,合并的任务.
ForkJoinTask是一个抽象类,它还有2个抽象子类:RecursiveAcion和RecursiveTask.其中RecursiveTask代表有返回值得任务.而RecursiveAction代表没有返回值.
类图如下:
package org.credo.thread.pool; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; import java.util.concurrent.TimeUnit; public class java7pool { public static void main(String[] args) throws InterruptedException { ForkJoinPool pool=new ForkJoinPool(); //提交可分解的PrintTask任务 pool.submit(new PrintTask(0, 30)); //关闭线程池 pool.shutdown(); pool.awaitTermination(2, TimeUnit.SECONDS); } } //我CPU是I5.双核四线程的CPU(虚拟四核实际双核). //所以ForkJoinPool会启动四个线程来执行这个打印任务. //而且不是连续打印,是因为程序将这个打印任务进行了分解.分解后并行执行任务. //RecursiveAction,没有返回值 class PrintTask extends RecursiveAction{ private static final long serialVersionUID = -7849183204551061688L; //每个"小任务"最多打印的数次 threshold 极限/临界强度 private static final int THRESHOLD=5; private int start; private int end; //打印从start到end的任务 public PrintTask(int start,int end){ this.start=start; this.end=end; } @Override protected void compute() { //当end与start之间的差小于threshold时,开始打印 if(end-start<THRESHOLD){ System.out.println("come here!"); for(int i=start;i<end;i++){ System.out.println(Thread.currentThread().getName()+"的i值:"+i+"==="+"start:"+start+" end:"+end); } }else{ System.out.println("here it is!"); //当end和start差大于threshold,即打印数超过threshold //将大任务分解成2个小任务 int middle=(start+end)/2; PrintTask left=new PrintTask(start, middle); PrintTask right=new PrintTask(middle, end); left.fork(); right.fork(); } } }
输出:
here it is! here it is! here it is! here it is! come here! here it is! come here! ForkJoinPool-1-worker-2的i值:11===start:11 end:15 here it is! here it is! come here! ForkJoinPool-1-worker-2的i值:12===start:11 end:15 ForkJoinPool-1-worker-4的i值:18===start:18 end:22 ForkJoinPool-1-worker-2的i值:13===start:11 end:15 ForkJoinPool-1-worker-3的i值:3===start:3 end:7 come here! ForkJoinPool-1-worker-3的i值:4===start:3 end:7 ForkJoinPool-1-worker-3的i值:5===start:3 end:7 ForkJoinPool-1-worker-2的i值:14===start:11 end:15 ForkJoinPool-1-worker-4的i值:19===start:18 end:22 come here! ForkJoinPool-1-worker-3的i值:6===start:3 end:7 ForkJoinPool-1-worker-1的i值:26===start:26 end:30 come here! ForkJoinPool-1-worker-2的i值:7===start:7 end:11 ForkJoinPool-1-worker-4的i值:20===start:18 end:22 ForkJoinPool-1-worker-2的i值:8===start:7 end:11 ForkJoinPool-1-worker-3的i值:0===start:0 end:3 ForkJoinPool-1-worker-1的i值:27===start:26 end:30 ForkJoinPool-1-worker-3的i值:1===start:0 end:3 ForkJoinPool-1-worker-2的i值:9===start:7 end:11 ForkJoinPool-1-worker-4的i值:21===start:18 end:22 ForkJoinPool-1-worker-2的i值:10===start:7 end:11 ForkJoinPool-1-worker-3的i值:2===start:0 end:3 ForkJoinPool-1-worker-1的i值:28===start:26 end:30 come here! come here! ForkJoinPool-1-worker-2的i值:22===start:22 end:26 ForkJoinPool-1-worker-1的i值:29===start:26 end:30 ForkJoinPool-1-worker-2的i值:23===start:22 end:26 ForkJoinPool-1-worker-4的i值:15===start:15 end:18 ForkJoinPool-1-worker-4的i值:16===start:15 end:18 ForkJoinPool-1-worker-4的i值:17===start:15 end:18 ForkJoinPool-1-worker-2的i值:24===start:22 end:26 ForkJoinPool-1-worker-2的i值:25===start:22 end:26
上面程序中的PrintTask left=new PrintTask(start, middle);
PrintTask right=new PrintTask(middle, end);
left.fork();
right.fork();
对指定的打印任务进行了分解.分解后的任务分别调用fork方法开始并行执行.执行后就如上面的打印信息.
从结果来看,ForkJoinPool启动了4个线程来执行.也可以看到程序打印的i的值不是连续的.因为程序把它进行了分解,分解后的任务会并行执行,不会按顺序打印.