Java5,Java7实现的<线程池>综述

jopen 11年前

系统启动一个新线程的成本是比较高的,因为它涉及和操作系统交互.在这种情况下,线程池可以很好的提升性能.尤其是程序中需要创建大量生存周期很短暂的线程时,更应该考虑线程池.

运行机制:与数据库连接池类似的是,线程池在系统启动时就创建大量空闲的线程,程序将一个Runnable或者Callable对象传给线程池,线程池就启动一个线程来执行他们的run或者call方法.当方法结束后,该线程不会死亡.而是再次返回线程池中成为空闲状态,等待下一个runnable/callable的run/call方法.

优点:使用线程池可以有效的控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能急剧下降,导致JVM崩溃.而线程池的最大线程数参数可以控制系统中并发线程数不超过此数.

Java 5实现的线程池

从Java5开始,Java内建支持线程池.Java5新增了一个Executors工厂类来产生线程池.如下

  1. newCachedTheradPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将被缓存在线程池中.
  2. newFixedTheradPool(int threadNum):创建一个可重用,有固定线程数的线程池.
  3. newSingleThreadExecutor():等同上面方法传入的参数为1.
  4. newScheduledThreadPool(int num):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务.num是指池中所保存的线程数,即使线程是空闲的也被保存在线程池内.
  5. newSingleThreadScheduledExecutor():创建只有一个线程的线程池,它可以在指定延迟后执行线程任务.
前3个方法返回都是一个ExecutorService对象,该对象代表一个线程池.而且是立即执行.
后2个方法返回都是一个ScheduledExecutorService对象,它是ExecutorService的子类,可以指定延迟后执行线程任务.
ExecutorService有三个方法:
  1. Future<?> submit(Runnable task):将一个Runnable对象提交给指定的线程池.Future<?>是代表Runnable的返回值,但run()方法没有返回值,所以是最后返回null.所以可以根据Future的isDone(),isCancelled()方法来获取Runnable对象的执行状态.
  2. <T>Future<T> submit(Runnable task,T result):同上,result将显式指定线程执行结束后的返回值,所以Future对象将在run方法执行结束后返回result.
  3. <T>Future<T> submit(Callable<T> task):将一个Callable对象提交给指定的线程池,其中Future表示Callable对象里call()方法的返回值.
ScheduledExecutorService代表的是指定延迟后或周期性的执行线程任务的线程池.提供四个方法:
  1. ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit):指定callable将在delay延迟后执行.
  2. ScheduledFuture<?> schedule(Runnable runnable,long delay,TimeUnit unit):指定runnable任务将在delay延迟后执行.
  3. ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable,long initialDelay,long period,TimeUnit unit):指定runnable任务在delay延迟后执行,而且以设定频率重复执行,也就是说,在initialDelay后开始执行,一次在initialDelay+period,initialDelay+2*period...处重复执行,以此类推.
  4. 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的实现类,因此是一种特殊的线程池.
它提供了如下两个常用的构造器.

  1. ForkJoinPool(int num):创建一个保护num个并行线程的ForkJoinPool.
  2. ForkJoinPool():以Runtime.availableProcessors()方法的返回值作为"num参数"来创建ForkJoinPool.
创建了ForkJoinPool实例之后,就可以调用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定任务了.
ForkJoinTask代表一个可以并行,合并的任务.
ForkJoinTask是一个抽象类,它还有2个抽象子类:RecursiveAcion和RecursiveTask.其中RecursiveTask代表有返回值得任务.而RecursiveAction代表没有返回值.

类图如下:
Java5,Java7实现的<线程池>综述

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的值不是连续的.因为程序把它进行了分解,分解后的任务会并行执行,不会按顺序打印.