Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理

  execute()是 java.util.concurrent.Executor接口中唯一的方法,JDK注释中的描述是“在未来的某一时刻执行命令command”,即向线程池中提交任务,在未来某个时刻执行,提交的任务必须实现Runnable接口,该提交方式不能获取返回值。下面是对execute()方法内部原理的分析,分析前先简单介绍线程池有哪些状态,在一系列执行过程中涉及线程池状态相关的判断。以下分析基于JDK 1.7

 

    以下是本文的目录大纲:

    一、线程池执行流程

    二、线程池状态

    三、任务提交内部原理

        1、execute()  --  提交任务

        2、addWorker()  --  添加worker线程

        3、内部类Worker

        4、runWorker()  --  执行任务

        5、getTask()  --  获取任务

        6、processWorkerExit()  --  worker线程退出



一、线程池的执行流程

Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理_第1张图片

1、如果线程池中的线程数量少于corePoolSize,就创建新的线程来执行新添加的任务
2、如果线程池中的线程数量大于等于corePoolSize,但队列workQueue未满,则将新添加的任务放到workQueue中
3、如果线程池中的线程数量大于等于corePoolSize,且队列workQueue已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务
4、如果线程池中的线程数量等于了maximumPoolSize,就用RejectedExecutionHandler来执行拒绝策略

二、线程池状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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;
 
// Packing and unpacking ctl
private  static  int  runStateOf( int  c)     { return  c & ~CAPACITY; }
private  static  int  workerCountOf( int  c)  { return  c & CAPACITY; }
private  static  int  ctlOf( int  rs, int  wc) { return  rs | wc; }

其中ctl这个AtomicInteger的功能很强大,其高3位用于维护线程池运行状态,低29位维护线程池中线程数量

1、RUNNING:-1<

2、SHUTDOWN:0<

3、STOP:1<

4、TIDYING:2<

5、TERMINATED:3<

这些状态均由int型表示,大小关系为 RUNNING

 

runStateOf(int c)  方法:c & 高3位为1,低29位为0的~CAPACITY,用于获取高3位保存的线程池状态

workerCountOf(int c)方法:c & 高3位为0,低29位为1的CAPACITY,用于获取低29位的线程数量

ctlOf(int rs, int wc)方法:参数rs表示runState,参数wc表示workerCount,即根据runState和workerCount打包合并成ctl

三、任务提交内部原理

1、execute()  --  提交任务

Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理_第2张图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
  * Executes the given task sometime in the future.  The task
  * may execute in a new thread or in an existing pooled thread.
  * 在未来的某个时刻执行给定的任务。这个任务用一个新线程执行,或者用一个线程池中已经存在的线程执行
  *
  * If the task cannot be submitted for execution, either because this
  * executor has been shutdown or because its capacity has been reached,
  * the task is handled by the current {@code RejectedExecutionHandler}.
  * 如果任务无法被提交执行,要么是因为这个Executor已经被shutdown关闭,要么是已经达到其容量上限,任务会被当前的RejectedExecutionHandler处理
  *
  * @param command the task to execute
  * @throws RejectedExecutionException at discretion of
  *         {@code RejectedExecutionHandler}, if the task
  *         cannot be accepted for execution                 RejectedExecutionException是一个RuntimeException
  * @throws NullPointerException if {@code command} is null
  */
public  void  execute(Runnable command) {
     if  (command == null )
         throw  new  NullPointerException();
     
     /*
      * Proceed in 3 steps:
      *
      * 1. If fewer than corePoolSize threads are running, try to
      * start a new thread with the given command as its first
      * task.  The call to addWorker atomically checks runState and
      * workerCount, and so prevents false alarms that would add
      * threads when it shouldn't, by returning false.
      * 如果运行的线程少于corePoolSize,尝试开启一个新线程去运行command,command作为这个线程的第一个任务
      *
      * 2. If a task can be successfully queued, then we still need
      * to double-check whether we should have added a thread
      * (because existing ones died since last checking) or that
      * the pool shut down since entry into this method. So we
      * recheck state and if necessary roll back the enqueuing if
      * stopped, or start a new thread if there are none.
      * 如果任务成功放入队列,我们仍需要一个双重校验去确认是否应该新建一个线程(因为可能存在有些线程在我们上次检查后死了) 或者 从我们进入这个方法后,pool被关闭了
      * 所以我们需要再次检查state,如果线程池停止了需要回滚入队列,如果池中没有线程了,新开启 一个线程
      *
      * 3. If we cannot queue task, then we try to add a new
      * thread.  If it fails, we know we are shut down or saturated
      * and so reject the task.
      * 如果无法将任务入队列(可能队列满了),需要新开区一个线程(自己:往maxPoolSize发展)
      * 如果失败了,说明线程池shutdown 或者 饱和了,所以我们拒绝任务
      */
     int  c = ctl.get();
     
     /**
      * 1、如果当前线程数少于corePoolSize(可能是由于addWorker()操作已经包含对线程池状态的判断,如此处没加,而入workQueue前加了)
      */
     if  (workerCountOf(c) < corePoolSize) {
         //addWorker()成功,返回
         if  (addWorker(command, true ))
             return ;
         
         /**
          * 没有成功addWorker(),再次获取c(凡是需要再次用ctl做判断时,都会再次调用ctl.get())
          * 失败的原因可能是:
          * 1、线程池已经shutdown,shutdown的线程池不再接收新任务
          * 2、workerCountOf(c) < corePoolSize 判断后,由于并发,别的线程先创建了worker线程,导致workerCount>=corePoolSize
          */
         c = ctl.get();
     }
     
     /**
      * 2、如果线程池RUNNING状态,且入队列成功
      */
     if  (isRunning(c) && workQueue.offer(command)) {
         int  recheck = ctl.get(); //再次校验位
         
         /**
          * 再次校验放入workerQueue中的任务是否能被执行
          * 1、如果线程池不是运行状态了,应该拒绝添加新任务,从workQueue中删除任务
          * 2、如果线程池是运行状态,或者从workQueue中删除任务失败(刚好有一个线程执行完毕,并消耗了这个任务),确保还有线程执行任务(只要有一个就够了)
          */
         //如果再次校验过程中,线程池不是RUNNING状态,并且remove(command)--workQueue.remove()成功,拒绝当前command
         if  (! isRunning(recheck) && remove(command))
             reject(command);
         //如果当前worker数量为0,通过addWorker(null, false)创建一个线程,其任务为null
         //为什么只检查运行的worker数量是不是0呢?? 为什么不和corePoolSize比较呢??
         //只保证有一个worker线程可以从queue中获取任务执行就行了??
         //因为只要还有活动的worker线程,就可以消费workerQueue中的任务
         else  if  (workerCountOf(recheck) == 0 )
             addWorker( null , false );  //第一个参数为null,说明只为新建一个worker线程,没有指定firstTask
                                      //第二个参数为true代表占用corePoolSize,false占用maxPoolSize
     }
     /**
      * 3、如果线程池不是running状态 或者 无法入队列
      *   尝试开启新线程,扩容至maxPoolSize,如果addWork(command, false)失败了,拒绝当前command
      */
     else  if  (!addWorker(command, false ))
         reject(command);
}

execute(Runnable command)

参数:
    command    提交执行的任务,不能为空
执行流程:
1、如果线程池当前线程数量少于corePoolSize,则addWorker(command, true)创建新worker线程,如创建成功返回,如没创建成功,则执行后续步骤;
    addWorker(command, true)失败的原因可能是:
    A、线程池已经shutdown,shutdown的线程池不再接收新任务
    B、workerCountOf(c) < corePoolSize 判断后,由于并发,别的线程先创建了worker线程,导致workerCount>=corePoolSize
2、如果线程池还在running状态,将task加入workQueue阻塞队列中,如果加入成功,进行double-check,如果加入失败(可能是队列已满),则执行后续步骤;
    double-check主要目的是判断刚加入workQueue阻塞队列的task是否能被执行
    A、如果线程池已经不是running状态了,应该拒绝添加新任务,从workQueue中删除任务
    B、如果线程池是运行状态,或者从workQueue中删除任务失败(刚好有一个线程执行完毕,并消耗了这个任务),确保还有线程执行任务(只要有一个就够了)
3、如果线程池不是running状态 或者 无法入队列,尝试开启新线程,扩容至maxPoolSize,如果addWork(command, false)失败了,拒绝当前command

 

2、addWorker()  --  添加worker线程

Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理_第3张图片 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
  * Checks if a new worker can be added with respect to current
  * pool state and the given bound (either core or maximum). If so,
  * the worker count is adjusted accordingly, and, if possible, a
  * new worker is created and started, running firstTask as its
  * first task. This method returns false if the pool is stopped or
  * eligible to shut down. It also returns false if the thread
  * factory fails to create a thread when asked.  If the thread
  * creation fails, either due to the thread factory returning
  * null, or due to an exception (typically OutOfMemoryError in
  * Thread#start), we roll back cleanly.
  * 检查根据当前线程池的状态和给定的边界(core or maximum)是否可以创建一个新的worker
  * 如果是这样的话,worker的数量做相应的调整,如果可能的话,创建一个新的worker并启动,参数中的firstTask作为worker的第一个任务
  * 如果方法返回false,可能因为pool已经关闭或者调用过了shutdown
  * 如果线程工厂创建线程失败,也会失败,返回false
  * 如果线程创建失败,要么是因为线程工厂返回null,要么是发生了OutOfMemoryError
  *
  * @param firstTask the task the new thread should run first (or
  * null if none). Workers are created with an initial first task
  * (in method execute()) to bypass(绕开) queuing when there are fewer
  * than corePoolSize threads (in which case we always start one),
  * or when the queue is full (in which case we must bypass queue).
  * Initially idle threads are usually created via
  * prestartCoreThread or to replace other dying workers.
  *
  * @param core if true use corePoolSize as bound, else
  * maximumPoolSize. (A boolean indicator is used here rather than a
  * value to ensure reads of fresh values after checking other pool
  * state).
  * @return true if successful
  */
private  boolean  addWorker(Runnable firstTask, boolean  core) {
     //外层循环,负责判断线程池状态
     retry:
     for  (;;) {
         int  c = ctl.get();
         int  rs = runStateOf(c); //状态
 
         // Check if queue empty only if necessary.
         /**
          * 线程池的state越小越是运行状态,runnbale=-1,shutdown=0,stop=1,tidying=2,terminated=3
          * 1、如果线程池state已经至少是shutdown状态了
          * 2、并且以下3个条件任意一个是false
          *   rs == SHUTDOWN         (隐含:rs>=SHUTDOWN)false情况: 线程池状态已经超过shutdown,可能是stop、tidying、terminated其中一个,即线程池已经终止
          *   firstTask == null      (隐含:rs==SHUTDOWN)false情况: firstTask不为空,rs==SHUTDOWN 且 firstTask不为空,return false,场景是在线程池已经shutdown后,还要添加新的任务,拒绝
          *   ! workQueue.isEmpty()  (隐含:rs==SHUTDOWN,firstTask==null)false情况: workQueue为空,当firstTask为空时是为了创建一个没有任务的线程,再从workQueue中获取任务,如果workQueue已经为空,那么就没有添加新worker线程的必要了
          * return false,即无法addWorker()
          */
         if  (rs >= SHUTDOWN &&
             ! (rs == SHUTDOWN &&
                firstTask == null  &&
                ! workQueue.isEmpty()))
             return  false ;
 
         //内层循环,负责worker数量+1
         for  (;;) {
             int  wc = workerCountOf(c); //worker数量
             
             //如果worker数量>线程池最大上限CAPACITY(即使用int低29位可以容纳的最大值)
             //或者( worker数量>corePoolSize 或  worker数量>maximumPoolSize ),即已经超过了给定的边界
             if  (wc >= CAPACITY ||
                 wc >= (core ? corePoolSize : maximumPoolSize))
                 return  false ;
             
             //调用unsafe CAS操作,使得worker数量+1,成功则跳出retry循环
             if  (compareAndIncrementWorkerCount(c))
                 break  retry;
             
             //CAS worker数量+1失败,再次读取ctl
             c = ctl.get();  // Re-read ctl
             
             //如果状态不等于之前获取的state,跳出内层循环,继续去外层循环判断
             if  (runStateOf(c) != rs)
                 continue  retry;
             // else CAS failed due to workerCount change; retry inner loop
             // else CAS失败时因为workerCount改变了,继续内层循环尝试CAS对worker数量+1
         }
     }
 
     /**
      * worker数量+1成功的后续操作
      * 添加到workers Set集合,并启动worker线程
      */
     boolean  workerStarted = false ;
     boolean  workerAdded = false ;
     Worker w = null ;
     try  {
         final  ReentrantLock mainLock = this .mainLock;
         w = new  Worker(firstTask); //1、设置worker这个AQS锁的同步状态state=-1
                                    //2、将firstTask设置给worker的成员变量firstTask
                                    //3、使用worker自身这个runnable,调用ThreadFactory创建一个线程,并设置给worker的成员变量thread
         final  Thread t = w.thread;
         if  (t != null ) {
             mainLock.lock();
             try  {
                 //--------------------------------------------这部分代码是上锁的
                 // Recheck while holding lock.
                 // Back out on ThreadFactory failure or if
                 // shut down before lock acquired.
                 // 当获取到锁后,再次检查
                 int  c = ctl.get();
                 int  rs = runStateOf(c);
 
                 //如果线程池在运行running
                 //worker数量-1的操作在addWorkerFailed()
                 if  (rs < SHUTDOWN ||
                     (rs == SHUTDOWN && firstTask == null )) {
                     if  (t.isAlive()) // precheck that t is startable   线程已经启动,抛非法线程状态异常
                         throw  new  IllegalThreadStateException();
                     
                     workers.add(w); //workers是一个HashSet
                     
                     //设置最大的池大小largestPoolSize,workerAdded设置为true
                     int  s = workers.size();
                     if  (s > largestPoolSize)
                         largestPoolSize = s;
                     workerAdded = true ;
                 }
               //--------------------------------------------
             }
             finally  {
                 mainLock.unlock();
             }
             
             //如果往HashSet中添加worker成功,启动线程
             if  (workerAdded) {
                 t.start();
                 workerStarted = true ;
             }
         }
     } finally  {
         //如果启动线程失败
         if  (! workerStarted)
             addWorkerFailed(w);
     }
     return  workerStarted;
}

addWorker(Runnable firstTask, boolean core)
参数:
    firstTask:    worker线程的初始任务,可以为空
    core:           true:将corePoolSize作为上限,false:将maximumPoolSize作为上限
addWorker方法有4种传参的方式:

    1、addWorker(command, true)

    2、addWorker(command, false)

    3、addWorker(null, false)

    4、addWorker(null, true)

在execute方法中就使用了前3种,结合这个核心方法进行以下分析
    第一个:线程数小于corePoolSize时,放一个需要处理的task进Workers Set。如果Workers Set长度超过corePoolSize,就返回false
    第二个:当队列被放满时,就尝试将这个新来的task直接放入Workers Set,而此时Workers Set的长度限制是maximumPoolSize。如果线程池也满了的话就返回false
    第三个:放入一个空的task进workers Set,长度限制是maximumPoolSize。这样一个task为空的worker在线程执行的时候会去任务队列里拿任务,这样就相当于创建了一个新的线程,只是没有马上分配任务
    第四个:这个方法就是放一个null的task进Workers Set,而且是在小于corePoolSize时,如果此时Set中的数量已经达到corePoolSize那就返回false,什么也不干。实际使用中是在prestartAllCoreThreads()方法,这个方法用来为线程池预先启动corePoolSize个worker等待从workQueue中获取任务执行
执行流程:
1、判断线程池当前是否为可以添加worker线程的状态,可以则继续下一步,不可以return false:
    A、线程池状态>shutdown,可能为stop、tidying、terminated,不能添加worker线程
    B、线程池状态==shutdown,firstTask不为空,不能添加worker线程,因为shutdown状态的线程池不接收新任务
    C、线程池状态==shutdown,firstTask==null,workQueue为空,不能添加worker线程,因为firstTask为空是为了添加一个没有任务的线程再从workQueue获取task,而workQueue为空,说明添加无任务线程已经没有意义
2、线程池当前线程数量是否超过上限(corePoolSize 或 maximumPoolSize),超过了return false,没超过则对workerCount+1,继续下一步
3、在线程池的ReentrantLock保证下,向Workers Set中添加新创建的worker实例,添加完成后解锁,并启动worker线程,如果这一切都成功了,return true,如果添加worker入Set失败或启动失败,调用addWorkerFailed()逻辑

 

3、内部类Worker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
  * Class Worker mainly maintains interrupt control state for
  * threads running tasks, along with other minor bookkeeping.
  * This class opportunistically extends AbstractQueuedSynchronizer
  * to simplify acquiring and releasing a lock surrounding each
  * task execution.  This protects against interrupts that are
  * intended to wake up a worker thread waiting for a task from
  * instead interrupting a task being run.  We implement a simple
  * non-reentrant mutual exclusion lock rather than use
  * ReentrantLock because we do not want worker tasks to be able to
  * reacquire the lock when they invoke pool control methods like
  * setCorePoolSize.  Additionally, to suppress interrupts until
  * the thread actually starts running tasks, we initialize lock
  * state to a negative value, and clear it upon start (in
  * runWorker).
  *
  * Worker类大体上管理着运行线程的中断状态 和 一些指标
  * Worker类投机取巧的继承了AbstractQueuedSynchronizer来简化在执行任务时的获取、释放锁
  * 这样防止了中断在运行中的任务,只会唤醒(中断)在等待从workQueue中获取任务的线程
  * 解释:
  *   为什么不直接执行execute(command)提交的command,而要在外面包一层Worker呢??
  *   主要是为了控制中断
  *   用什么控制??
  *   用AQS锁,当运行时上锁,就不能中断,TreadPoolExecutor的shutdown()方法中断前都要获取worker锁
  *   只有在等待从workQueue中获取任务getTask()时才能中断
  * worker实现了一个简单的不可重入的互斥锁,而不是用ReentrantLock可重入锁
  * 因为我们不想让在调用比如setCorePoolSize()这种线程池控制方法时可以再次获取锁(重入)
  * 解释:
  *   setCorePoolSize()时可能会interruptIdleWorkers(),在对一个线程interrupt时会要w.tryLock()
  *   如果可重入,就可能会在对线程池操作的方法中中断线程,类似方法还有:
  *   setMaximumPoolSize()
  *   setKeppAliveTime()
  *   allowCoreThreadTimeOut()
  *   shutdown()
  * 此外,为了让线程真正开始后才可以中断,初始化lock状态为负值(-1),在开始runWorker()时将state置为0,而state>=0才可以中断
  *
  *
  * Worker继承了AQS,实现了Runnable,说明其既是一个可运行的任务,也是一把锁(不可重入)
  */
private  final  class  Worker
     extends  AbstractQueuedSynchronizer
     implements  Runnable
{
     /**
      * This class will never be serialized, but we provide a
      * serialVersionUID to suppress a javac warning.
      */
     private  static  final  long  serialVersionUID = 6138294804551838833L;
 
     /** Thread this worker is running in.  Null if factory fails. */
     final  Thread thread; //利用ThreadFactory和 Worker这个Runnable创建的线程对象
     
     /** Initial task to run.  Possibly null. */
     Runnable firstTask;
     
     /** Per-thread task counter */
     volatile  long  completedTasks;
 
     /**
      * Creates with given first task and thread from ThreadFactory.
      * @param firstTask the first task (null if none)
      */
     Worker(Runnable firstTask) {
         //设置AQS的同步状态private volatile int state,是一个计数器,大于0代表锁已经被获取
         setState(- 1 ); // inhibit interrupts until runWorker
                       // 在调用runWorker()前,禁止interrupt中断,在interruptIfStarted()方法中会判断 getState()>=0
         this .firstTask = firstTask;
         this .thread = getThreadFactory().newThread( this ); //根据当前worker创建一个线程对象
                                                           //当前worker本身就是一个runnable任务,也就是不会用参数的firstTask创建线程,而是调用当前worker.run()时调用firstTask.run()
     }
 
     /** Delegates main run loop to outer runWorker  */
     public  void  run() {

你可能感兴趣的:(java)