一,介绍

  类视图如下:

 

  自Java 1.5后,Java对线程相关的库做了很大的拓展,线程池就是其中之一。Java线程的新特性多数在java.util.concurrent,其包含众多的接口和类。其中java.util.concurrent.Executor是这些类的最顶级接口。其是执行线程的一个工具。ExecutorService在Executor的基础上增加了一些方法,以向线程池提交任务。包括实现Runnable接口的任务和实现Callable接口的任务。两个方法为:

  Future<?> submit(Runnable task)

  Future<T> submit(Callable<T> task)

  实现Runnable接口的任务,其run方法的返回值为void,因此submit的返回值为Future<?>类型,?表示匹配任意一种类型。实现Callable<T>接口的任务,其call方法的返回值类型为T,对应submit的返回值类型则是Future<T>。具有返回值的Callable的任务对于多个线程中传递状态和结果是非常有用的。另外使用这两个方法返回的Future对象可以阻塞当前线程直到提交的任务运行完毕以获取结果,也可以用以取消任务的执行,或者检测任务是否被取消或者是否执行完毕。如果不使用Future,我们检测一个线程是否执行完毕通常使用Thread.join()或者对状态标识进行轮询。

  阻塞等待结果,或者在某个时候获取结果的方法是,调用Futrue的get()方法。这样调用线程便会等待在线程执行完成并将结果反馈到Futrue对象。之后调用线程可以对处理结果进行判别处理。isDone(),isCancelled()可以用以判断当前的任务执行状态(注意被打断或取消也是Done的一种)。cancel(Boolean)方法则可以用以取消线程执行。如果参数是true表示即使任务已经执行也将试图取消它,如果是false则表示如果任务没有被执行则取消,执行了则不取消执行。

  可以使用Executor的静态方法创建实现ExecutorService的线程池类。对应方法创建的线程池描述如下:

  1,newSingleThreadExecutor,单线程线程池。单个线程执行器,内有一个线程执行Runnable的任务。如果该线程发生异常终了,则创建新的进行补充。可以保证任务顺 序的执行。

  2,newFixedThreadPool,创建固定线程数目(参数指定)的线程池。每加入一个任务,创建一个线程,直到达到固定数目后,将会有固定大小的线程执行Runnable的任务。如果有线程异常终了,则创建新的线程来进行补充。

  3,newCachedThreadPool, 缓冲线程池,产生一个大小可变的线程池。当线程池的线程多于执行任务所需要的线程的时候,对空闲线程(即60s没有任务执行)进行回收;当执行任务的线程数不足的时候,自动拓展线程数量。因此线程数量是JVM可创建线程的最大数目。

  4,newSingleThreadScheduledExecutor,单个线程调度执行器,产生单个线程执行任务,采用schedule方法可以延期或者定期的执行任务

  5,newScheduledThreadPool,调度线程执行器,当有任务的时候,创建线程,直到默认线程数量(参数指定),当执行任务所需的线程多于该数目的时候,自动拓展线程数量,没有上限;如果线程空闲则被回收,直到默认线程数量。

  6,另外可以自定义线程池,请参见使用代码例子。

  另外注意CompletionService接口,其能够根据任务的执行先后顺序得到执行结果。


二,使用例子


//两种任务
package poolmanager;
 public class RunnableTask implements Runnable {

     int i = 0;

     public RunnableTask(int i) {

         this.i = i;

     }

     public void run() {

         System.out.println("******************");
         System.out.println(Thread.currentThread().getName());

         try {
             Thread.sleep(10000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }

         System.out.println("Task is :" + i);

     }

 }package poolmanager;
 import java.util.concurrent.Callable;

 public  class CallableTask implements Callable<String> {

     public String ok = "OK!";
     public String ng = "Error Happened in Running!";

     int i = 0;

     public CallableTask(int i){
         this.i = i;
     }

     public String call(){

         System.out.println("******************");
         System.out.println(Thread.currentThread().getName());

         try {
             Thread.sleep(10000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }

         System.out.println("Task is :" + i);

         return ok;

     }

 }//线程管理类
package poolmanager;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;

 public class JavaThreadPoolManager {

     int ThreadSize = 2;
     int ThreadMaxSize = 2*ThreadSize;

     ExecutorService executorSingleThreadExecutor = null;
     ExecutorService executorFixedThreadPool = null;
     ExecutorService executorCachedThreadPool = null;
     ScheduledExecutorService executorSingleThreadScheduledExcutor = null;
     ScheduledExecutorService executorScheduledThreadPool = null;

     //自定义线程池
     BlockingQueue<Runnable> bqueue = null;
     ThreadPoolExecutor pool = null;

     public void startJavaThreadPoolManager(){


         //单个线程执行器,内有一个线程执行Runnable的任务。
         //如果该线程发生异常终了,则创建新的进行补充。
         //可以保证任务顺序的执行。
         executorSingleThreadExecutor = Executors.newSingleThreadExecutor();

         //固定数目的线程执行器。
         //每加入一个任务,创建一个线程,直到达到固定数目后,
         //将会有固定大小的线程执行Runnable的任务。
         //如果有线程异常终了,则创建新的线程来进行补充。
         executorFixedThreadPool = Executors.newFixedThreadPool(ThreadSize);

         //缓冲线程执行器,产生一个大小可变的线程池。
         //当线程池的线程多于执行任务所需要的线程的时候,
         //对空闲线程(即60s没有任务执行)进行回收;
         //当执行任务的线程数不足的时候,自动拓展线程数量。因此线程数量是JVM
         //可创建线程的最大数目。
         executorCachedThreadPool = Executors.newCachedThreadPool();

         //单个线程调度执行器,产生单个线程执行任务,采用schedule方法可以延期或者定期的执行任务
         executorSingleThreadScheduledExcutor = Executors
             .newSingleThreadScheduledExecutor();

         //调度线程执行器,当有任务的时候,创建线程,直到默认线程数量(参数指定),
         //当执行任务所需的线程多于该数目的时候,
         //自动拓展线程数量,没有上限;
         //如果线程空闲则被回收,直到默认线程数量。
         executorScheduledThreadPool = Executors.newScheduledThreadPool(ThreadSize);

         //自定义线程池
         bqueue = new ArrayBlockingQueue<Runnable>(20);
         //第三个参数是线程池线程所允许的空闲时间
         //第四个参数是线程池线程空闲时间的单位
         //第五个参数是缓冲任务队列
         //第六个参数是缓冲区满的时候,对任务的处理策略
         //第六个参数有如下几种选择:
         //ThreadPoolExecutor.AbortPolicy()
         //抛出java.util.concurrent.RejectedExecutionException异常
         //ThreadPoolExecutor.CallerRunsPolicy()
         //重试添加当前的任务,即自动以任务为参数,再次调用execute()方法
         //ThreadPoolExecutor.DiscardOldestPolicy()
         //抛弃一个已有的任务(抛弃当前任务队列的头部任务,即最开始加入的任务)
         //ThreadPoolExecutor.DiscardPolicy()
         //抛弃当前这个任务

         pool = new ThreadPoolExecutor(
                 ThreadSize,ThreadMaxSize,2,TimeUnit.MILLISECONDS,bqueue
                 ,new ThreadPoolExecutor.DiscardOldestPolicy());

     }

     public void endJavaThreadPoolManager(){

         executorSingleThreadExecutor.shutdown();
         executorFixedThreadPool.shutdown();
         executorCachedThreadPool.shutdown();
         executorSingleThreadScheduledExcutor.shutdown();
         executorScheduledThreadPool.shutdown();
         pool.shutdown();

     }

 }//使用
package poolmanager;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;

 public class MainThread {

     public static void main(String[] args) throws InterruptedException {

         JavaThreadPoolManager jThreadPoolManager = new JavaThreadPoolManager();

         jThreadPoolManager.startJavaThreadPoolManager();

         RunnableTask command1 = new RunnableTask(1);
         CallableTask command2 = new CallableTask(2);

         jThreadPoolManager.executorSingleThreadExecutor.execute(command1);
         jThreadPoolManager.executorSingleThreadExecutor.submit(command2);

         jThreadPoolManager.executorFixedThreadPool.execute(command1);
         jThreadPoolManager.executorFixedThreadPool.submit(command2);

         jThreadPoolManager.executorCachedThreadPool.execute(command1);
         jThreadPoolManager.executorCachedThreadPool.submit(command2);

         jThreadPoolManager.executorScheduledThreadPool.execute(command1);
         jThreadPoolManager.executorScheduledThreadPool.submit(command2);

         jThreadPoolManager.executorSingleThreadScheduledExcutor.execute(command1);
         jThreadPoolManager.executorSingleThreadScheduledExcutor.submit(command2);

         jThreadPoolManager.executorScheduledThreadPool.execute(command1);
         jThreadPoolManager.executorScheduledThreadPool.schedule(command1,60, TimeUnit.SECONDS);
         jThreadPoolManager.executorScheduledThreadPool.schedule(command2,60, TimeUnit.SECONDS);

         jThreadPoolManager.executorSingleThreadScheduledExcutor.scheduleWithFixedDelay(command1, 0, 20, TimeUnit.SECONDS);

         Future<String> returnFuture =
             jThreadPoolManager.executorSingleThreadScheduledExcutor.submit(command2);

         System.out.println(returnFuture.isCancelled());
         System.out.println(returnFuture.isDone());

         returnFuture.cancel(true);

         System.out.println(returnFuture.isCancelled());
         System.out.println(returnFuture.isDone());

         String resultString =null;

         if(!returnFuture.isCancelled()){

             try {
                 resultString = returnFuture.get();
             } catch (ExecutionException e) {
                 e.printStackTrace();
             }

             System.out.println(resultString);

         }

         jThreadPoolManager.pool.execute(command1);

     }

 }

三,再讨论


  我们看到自定义线程池的时候,可以自定义任务队列,其中有如下几种任务队列的自定义方法:

SynchronousQueue的,则默认的队列大小为1,在这种情况下,如果没有空闲线程,则往往直接创建新的线程执行该任务,因此往往需要不设立线程的数目上限。该策略可以避免在处理可能具有内部依赖的请求集时出现锁,因为其任务执行顺序必然是先加入的先被执行。

  2,无界队列 选择任务队列为LinkedBlockingQueue 其意义即任务队列大小无限制。这样创建的线程往往不超过corePoolSize。因此maximumPoolSize的值就没有什么意义。适合于任务相互独立。

  3,有界队列 使用具有最大线程数目限制maximumPoolSize的时候,有界队列例如ArrayBlockingQueue有助于防止资源耗尽,但可能较难控制。

  使用大队列小池子有利于降低CPU使用,上下文切换,但是吞吐量不高;如果小队列大池子,则CPU使用率高,但是可能调度开销大,也会降低吞吐量。

   另外,选择上述任务队列的方案,需要与corePoolSize和maximumPoolSizes这对参数配合。

 

  因此,线程池的选择要根据任务的类型,比如任务是数量大、单个执行时间短,还是任务数量小、单个执行长,还是任务数量又多,执行时间又长,来进行不同的选择。而自定义线程池也是需要根据任务的类型选择任务队列、corePoolSize和maximumPoolSizes。

maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。如果很容易就产生线程的回收,也会导致性能下降。