ThreadPoolExecutor源码解析

 

线程池,在java开发过程当中都不可避免的遇到,而在创建启动回收过程时需要从用户态切换到内核态,这样造成频繁的切换上下文,cup性能造成了损耗。Doug Lea为此开发了一种连接池容器。我理解的是牺牲空间换取时间的方案。因为在创建线程和销毁线程其实都是需要消耗时间的。事先为应用创建线程。这样在启动过程当中相当于在池中拿取,无需创建。我们从一下几个步骤来分析线程池:

线程池如何创建?

线程池的启动?

线程池调度策略?

线程池阻塞队列容器的选择?

线程池拒绝策略 ?

图解线程池的一些关键步骤。

 

 

在了解线程池构造方法时候我们先要理解几个重要线程池的重要线程变量

   
   
    //线程安全存储线程池的工作状态以及线程池当中正在运行的线程数,高位3bit存储线程池 
    //状态,低位29bit用于存储正在运行的线程数
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits  高位3bit表示的线程状态
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    //获取线程池的状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
   //获取正在运行的线程数
    private static int workerCountOf(int c)  { return c & CAPACITY; }
   //初始化ctl
    private static int ctlOf(int rs, int wc) { return rs | wc; }
private final BlockingQueue<Runnable> workQueue;              //任务缓存队列,用来存放等待执行的任务 
private final ReentrantLock mainLock = new ReentrantLock();   //线程池的主要状态锁,对线程池状态(比如线程池大小
                                                              //、runState等)的改变都要使用这个锁
private final HashSet<Worker> workers = new HashSet<Worker>();  //用来存放工作集
 
private volatile long  keepAliveTime;    //线程存货时间   
private volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间
private volatile int   corePoolSize;     //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
private volatile int   maximumPoolSize;   //线程池最大能容忍的线程数
 
private volatile int   poolSize;       //线程池中当前的线程数
 
private volatile RejectedExecutionHandler handler; //任务拒绝策略
 
private volatile ThreadFactory threadFactory;   //线程工厂,用来创建线程
 
private int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数
 
private long completedTaskCount;   //用来记录已经执行完毕的任务个数

构造方法

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue); 
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);

通过构造方法我们得到如下几个重要参数

  • corePoolSize:核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
  • maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程;
  • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
  • unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:具体看api即可
  • workQueue:一个阻塞队列,用来存储等待执行的任务 

       常用的队列有以下几个 

ArrayBlockingQueue 有固定宽度队列
LinkedBlockingQueue;链表队列
SynchronousQueue;没有容器的队列
DelayQueue:延时队列
PriorityBlockingQueue:基于优先的队列

这几种队列的实现方式决定了阻塞队列的阻塞策略。

拒绝策略:

AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
DiscardPolicy:也是丢弃任务,但是不抛出异常。 
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
CallerRunsPolicy:由调用线程处理该任务 

 

posted on 2018-07-20 17:13  chengjiawolaila  阅读(130)  评论(0)    收藏  举报

导航