The thread pool technology used in Java generally uses Executors
this factory class, which provides a very simple method to create various types of thread pools:
public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newSingleThreadExecutor() public static ExecutorService newCachedThreadPool() public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
The core interface in fact Executor
, it is only a execute
method of abstraction to perform a task (Runnable Interface), the ExecutorService
interface Executor
provides management of life-cycle tasks performed on the basis of, mainly submit
, and shutdown
methods AbstractExecutorService
of ExecutorService
some of the ways to do a default implementation , Mainly the submit and invoke methods, and the Executor interface execute
method of the real task execution is implemented by subclasses, that is ThreadPoolExecutor
, it implements the task execution framework based on the thread pool, so to understand the thread pool of the JDK, then you have to look at this first class.
execute
Before looking at the method, we need to introduce a few variables or classes.
ctl
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
This variable is the core of the entire class, which AtomicInteger
ensures that the operation of this variable is atomic. Through clever operations, ThreadPoolExecutor saves two contents with this variable:
- The number of all active threads
- The state of each thread (runState)
The lower 29 bits store the number of threads and the upper 3 bits store runState
, so runState has 5 values:
- RUNNING:-536870912
- SHUTDOWN:0
- STOP:536870912
- TIDYING:1073741824
- TERMINATED:1610612736
The transition between the various states in the thread pool is more complicated, and you can mainly remember the following:
- RUNNING state: the thread pool is running normally, can accept new tasks and process tasks in the queue;
- SHUTDOWN state: no longer accept new tasks, but will execute the tasks in the queue;
- STOP state: no longer accept new tasks, do not process tasks in the queue
There are some operations around ctl variables. Understanding these methods is the basis for understanding some obscure code behind:
corePoolSize
The size of the core thread pool. If the active thread is smaller than corePoolSize, it will be created directly.
keepAliveTime
The thread gets the timeout time of the task from the queue, that is, if the thread is idle for more than this time, it will be terminated.
Worker
private final class Worker extends AbstractQueuedSynchronizer implements Runnable ...
内部类Worker
是对任务的封装,所有submit的Runnable都被封装成了Worker,它本身也是一个Runnable, 然后利用AQS框架(关于AQS可以看我这篇文章)实现了一个简单的非重入的互斥锁, 实现互斥锁主要目的是为了中断的时候判断线程是在空闲还是运行,可以看后面shutdown
和shutdownNow
方法的分析。
// state只有0和1,互斥 protected boolean tryAcquire(int unused) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true;// 成功获得锁 } // 线程进入等待队列 return false; } protected boolean tryRelease(int unused) { setExclusiveOwnerThread(null); setState(0); return true; }
之所以不用ReentrantLock是为了避免任务执行的代码中修改线程池的变量,如setCorePoolSize
,因为ReentrantLock是可重入的。
execute
execute
方法主要三个步骤:
- 活动线程小于corePoolSize的时候创建新的线程;
- 活动线程大于corePoolSize时都是先加入到任务队列当中;
- 任务队列满了再去启动新的线程,如果线程数达到最大值就拒绝任务。
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); // 活动线程数 < corePoolSize if (workerCountOf(c) < corePoolSize) { // 直接启动新的线程。第二个参数true:addWorker中会重新检查workerCount是否小于corePoolSize if (addWorker(command, true)) // 添加成功返回 return; c = ctl.get(); } // 活动线程数 >= corePoolSize // runState为RUNNING && 队列未满 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // double check // 非RUNNING状态 则从workQueue中移除任务并拒绝 if (!isRunning(recheck) && remove(command)) reject(command);// 采用线程池指定的策略拒绝任务 // 线程池处于RUNNING状态 || 线程池处于非RUNNING状态但是任务移除失败 else if (workerCountOf(recheck) == 0) // 这行代码是为了SHUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。 // 添加一个null任务是因为SHUTDOWN状态下,线程池不再接受新任务 addWorker(null, false); // 两种情况: // 1.非RUNNING状态拒绝新的任务 // 2.队列满了启动新的线程失败(workCount > maximumPoolSize) } else if (!addWorker(command, false)) reject(command); }
注释比较清楚了就不再解释了,其中比较难理解的应该是addWorker(null, false);
这一行,这要结合addWorker一起来看。 主要目的是防止HUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。
addWorker
这个方法理解起来比较费劲。
runWorker
任务添加成功后实际执行的是runWorker
这个方法,这个方法非常重要,简单来说它做的就是:
- 第一次启动会执行初始化传进来的任务firstTask;
- 然后会从workQueue中取任务执行,如果队列为空则等待keepAliveTime这么长时间。
getTask
processWorkerExit
线程退出会执行这个方法做一些清理工作。
tryTerminate
processWorkerExit方法中会尝试调用tryTerminate来终止线程池。这个方法在任何可能导致线程池终止的动作后执行:比如减少wokerCount或SHUTDOWN状态下从队列中移除任务。
shutdown和shutdownNow
shutdown这个方法会将runState置为SHUTDOWN
,会终止所有空闲的线程。
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); // 线程池状态设为SHUTDOWN,如果已经至少是这个状态那么则直接返回 advanceRunState(SHUTDOWN); // 注意这里是中断所有空闲的线程:runWorker中等待的线程被中断 → 进入processWorkerExit → // tryTerminate方法中会保证队列中剩余的任务得到执行。 interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); }
shutdownNow方法将runState置为STOP。和shutdown方法的区别,这个方法会终止所有的线程。
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); // STOP状态:不再接受新任务且不再执行队列中的任务。 advanceRunState(STOP); // 中断所有线程 interruptWorkers(); // 返回队列中还没有被执行的任务。 tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }
主要区别在于shutdown调用的是interruptIdleWorkers
这个方法,而shutdownNow实际调用的是Worker类的interruptIfStarted
方法:
private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) { Thread t = w.thread; // w.tryLock能获取到锁,说明该线程没有在运行,因为runWorker中执行任务会先lock, // 因此保证了中断的肯定是空闲的线程。 if (!t.isInterrupted() && w.tryLock()) { try { t.interrupt(); } catch (SecurityException ignore) { } finally { w.unlock(); } } if (onlyOne) break; } } finally { mainLock.unlock(); } }
void interruptIfStarted() { Thread t; // 初始化时state == -1 if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } }
这就是前面提到的Woker类实现AQS的主要作用。
注意:shutdown方法可能会在finalize被隐式的调用。
这篇博客基本都是代码跟注释,所以如果不是分析ThreadPoolExecutor
源码的话看起来会非常无聊。