Java线程池之ThreadPoolExecutor
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。
1.ThreadPoolExecutor类的构造函数:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); } public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
使用ThreadPoolExecutor类的构造函数来创建实例时,需要指定一些参数。而这些参数即是线程池的关键属性。
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数 = 核心线程数 + 非核心线程数
- keepAliveTime:空闲线程的存活时间
- unit:时间单位
- workQueue:任务队列
- threadFactory:线程工厂,默认为Executors.defaultThreadFactory()
- handler:拒绝策略,默认是AbortPolicy中止策略,直接抛出异常,调用者捕捉异常自行处理。
2.线程池的属性
核心线程数
private volatile int corePoolSize;
最大线程数 = 核心线程数 + 非核心线程数
private volatile int maximumPoolSize;
是否需要限制空闲核心线程的存活时间
private volatile boolean allowCoreThreadTimeOut;
如果allowCoreThreadTimeOut == false,那么核心线程即使空闲着,也会保持存活(默认)。
如果allowCoreThreadTimeOut == true ,那么核心线程的空闲时间一旦超过了keepAliveTime,就会被终止。
空闲线程的存活时间
private volatile long keepAliveTime;
可选的时间单位:
- TimeUnit.NANOSECONDS 纳秒
- TimeUnit.MICROSECONDS 微秒
- TimeUnit.MILLISECONDS 毫秒
- TimeUnit.SECONDS 秒
- TimeUnit.MINUTES 分钟
- TimeUnit.HOURS 小时
如果 keepAliveTime == 0 ,那么线程的存活时间没有限制。
如果 keepAliveTime > 0 ,非核心线程的空闲时间如果超过了keepAliveTime,就会被终止。
如果 keepAliveTime > 0 && allowCoreThreadTimeOut == true ,那么线程池中所有线程,只要其空闲时间如果超过了keepAliveTime,都会被终止。
任务队列
private final BlockingQueue<Runnable> workQueue;
线程工厂
private volatile ThreadFactory threadFactory;
任务拒绝策略
private volatile RejectedExecutionHandler handler;
可选的任务拒绝策略有:
- ThreadPoolExecutor.AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
线程池中曾出现的最大worker数(也可以说下最大线程数)
private int largestPoolSize;
线程池已经执行的任务数
private long completedTaskCount;
3.线程池的运行状态
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;
- RUNNING:允许提交并处理任务,一个线程池时初始化状态为RUNNING。
- SHUTDOWN:不允许提交新的任务,但是会处理完已提交的任务。
- STOP:不允许提交新的任务,也不会处理阻塞队列中未执行的任务,并设置正在执行的线程的中断标志位。
- TIDYING:所有任务执行完毕,池中工作的线程数为0,等待执行terminated()勾子方法。
- TERMINATED:terminated()勾子方法执行完毕。
线程池是如何存储、读取、更新运行状态和worker(线程)数的呢???
线程池的运行状态和worker数都存储在一个原子Integer类的实例对象中。
private static int ctlOf(int rs, int wc) { return rs | wc; } private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
int类型,占4个字节,即32个bit。其中高3位存储了运行状态,低29位存储了线程池中的worker数。
更新线程池中的worker数
// worker数+1 private boolean compareAndIncrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect + 1); } // worker数-1 private boolean compareAndDecrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect - 1); } // worker数-1 失败则重试,直至成功 private void decrementWorkerCount() { do {} while (! compareAndDecrementWorkerCount(ctl.get())); }
获取线程池的运行状态和worker数
private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 以下才是关键 // 获取线程池当前的运行状态 private static int runStateOf(int c) { return c & ~CAPACITY; } // 获取线程池中当前的worker数 private static int workerCountOf(int c) { return c & CAPACITY; }
5.线程池的任务拒绝策略
AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
DiscardPolicy:也是丢弃任务,但是不抛出异常。
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {} }
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
CallerRunsPolicy:由调用线程处理该任务
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
6.线程池执行任务的全过程
在ThreadPoolExecutor类中,最核心的任务提交方法是execute()方法,虽然通过submit也可以提交任务,但是实际上submit方法里面最终调用的还是execute()方法,所以我们只需要研究execute()方法的实现原理即可。
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); // 获取线程池的运行状态和worker数 int c = ctl.get(); // 判断当前线程池中的线程数是否小于核心线程数。 if (workerCountOf(c) < corePoolSize) { // 如果线程池中的worker数小于核心线程数 // 就新建一个核心Worker对象,添加到workers中,添加成功后直接返回 if (addWorker(command, true)) { return; } // 失败则重新获取线程池的运行状态和worker数 c = ctl.get(); } // 能走到此处,说明当前线程池中的worker数已经 >= corePoolSize。 // 检查线程池是否处于RUNNING状态,如果是,就把任务对象command添 // 加到任务队列workQueue之中。此处调用的是offer()方法添加任务对 // 象到任务队列,如果没能添加成功,会立即返回结果false,不会阻塞线程。 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (!isRunning(recheck) && remove(command)) { // 如果当前线程池已经不处于RUNNING状态,就移除任务队列workQueue // 之中的任务对象command,并执行拒绝策略 reject(command); } else if (workerCountOf(recheck) == 0) { // 如果当前线程池中的worker数为0,则直接创建一个非核心线程, // 任务从任务队列获取。 addWorker(null, false); } } else if (!addWorker(command, false)) { // 如果添加任务对象command到任务队列workQueue失败,就创建一个 // 非核心的Worker对象,添加到workers中。 // 添加失败,就执行拒绝策略 reject(command); } }
过程简述:
- 如果poolSize < corePoolSize ,那就创建核心线程、启动、执行任务,最后直接返回
- 如果poolSize == corePoolSize,那就把任务添加到任务队列workQueue中
- 如果任务队列已满,且poolSize < maximumPoolSize ,就创建非核心线程、启动、执行任务
- 如果poolSize == maximumPoolSize,那就采用拒绝策略来拒绝任务
执行execute()方法,在线程池接受了任务对象的前提下,并没有直接新建线程,而是调用addWorker(command, true)方法。
addWorker(command, true)方法中涉及到了Worker类,所以我们先来看下Worker类的源码。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable { private static final long serialVersionUID = 6138294804551838833L; /** 执行任务的线程类对象 */ final Thread thread; /** 需要线程执行的任务,可以是null */ Runnable firstTask; /** 执行的任务数 */ volatile long completedTasks; Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; // 将worker对象自身传入到线程。 // 线程启动后,就会执行worker对象的run()方法 this.thread = getThreadFactory().newThread(this); } public void run() { runWorker(this); } // state==0 代表的是解锁状态 // state==1 代表的是被锁状态 protected boolean isHeldExclusively() { return getState() != 0; } // 抢占锁 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; } public void lock() { acquire(1); } public boolean tryLock() { return tryAcquire(1); } public void unlock() { release(1); } public boolean isLocked() { return isHeldExclusively(); } // 中断运行中的线程 void interruptIfStarted() { Thread t; if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } } }
Worker是ThreadPoolExecutor类中的一个私有内部类,继承了AQS,实现了 Runnable接口。提供了一种控制线程中断的机制。
下面看下addWorker(Runnable firstTask, boolean core)方法的过程
/** * 新建worker * @param firstTask 线程执行的任务 * 如果firstTask==null,那么线程从任务队列获取任务执行 * @param core 是否是核心线程 * 如果core==true,创建核心Worker,反之创建非核心Worker * @return true if successful */ private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); // 线程池的运行状态 int rs = runStateOf(c); /** * 线程池的state越小越是运行状态,running=-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; } for (;;) { // 当前线程池中的线程数 int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) { // 如果worker数量>线程池最大上限CAPACITY(即使用int低29位可以容纳的最大值) // 如果线程池中的线程数已经大于或等于线程池中线程数的边界值,返回false // 线程数边界值: // 如果创建的是核心线程(参数core=true),边界值是corePoolSize // 如果创建的是非核心线程(参数core=false),边界值是maximumPoolSize return false; } if (compareAndIncrementWorkerCount(c)) { // 调用unsafe CAS操作,使得worker数量+1,成功则跳出retry循环 break retry; } c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) { // 如果状态不等于之前获取的state,跳出内层循环,继续去外层循环判断 continue retry; } else { // CAS失败是因为workerCount改变了,继续内层循环尝试CAS对worker数量+1 } } } // 新建的线程是否被成功启动的标志 boolean workerStarted = false; // 封装了firstTask的Worker对象是否被成功添加到workers的标志 boolean workerAdded = false; Worker w = null; try { final ReentrantLock mainLock = this.mainLock; w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { mainLock.lock(); try { int c = ctl.get(); int rs = runStateOf(c); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) { // 预先检查线程t是否已经启动了 // 如果是则抛出IllegalThreadStateException异常 throw new IllegalThreadStateException(); } workers.add(w); // 如果当前线程池中的workers数(也可以说是线程数)s超过了历史最大的worker数largestPoolSize,更新largestPoolSize=s int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { // 如果线程被成功添加到workers中,就启动线程并设置workerStarted=true t.start(); // 线程启动后,调用worker对象w的run(),进而调用runWorker(); workerStarted = true; } } } finally { if (! workerStarted) { // 线程启动出现异常 addWorkerFailed(w); } } return workerStarted; } /** * 线程启动出现异常,将worker对象w从workers移除, * 再调用unsafe CAS操作,使得worker数量-1 */ private void addWorkerFailed(Worker w) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (w != null) { workers.remove(w); } decrementWorkerCount(); tryTerminate(); } finally { mainLock.unlock(); } }
过程简述:
- 检查线程池当前的运行状态和worker数,判断此时是否能够添加worker对象,如果不能,return false;
- 如果步骤一判断的结果是可以添加,那就尝试把线程池中的worker数+1,失败就循环执行步骤1,2,直至worker数+1成功
- 创建worker对象,将任务对象赋给了worker对象的成员变量firstTask
- 将worker对象添加到workers中,并判断当前的worker数有没超过历史最大的worker数,如果有,更新largestPoolSize
- 启动worker对象中线程成员变量thread,成功启动就返回workerStarted=true,失败则回滚之前的操作调用addWorkerFailed(w)
阅读Worker类的代码我们可以发现,启动worker对象中线程成员变量thread之后,会先执行worker对象中run方法(新建的线程传入的Runnable对象就是worker对象本身),而在run方法中,只有一行代码,调用runWorker(Worker w)。下面让我们来看看runWorker(Worker w)的源码。
final void runWorker(Worker w) { // 获取当前线程wt Thread wt = Thread.currentThread(); // 取出Worker对象w中封装的任务 Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // 此时允许中断线程任务 boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { // 如果任务task不为null,就执行该任务task // 如果任务task为null,就调用getTask()方法从任务队列中获取任务并执行 w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. // This requires a recheck in second case to deal with shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) { // 当前线程池的运行状态是 STOP,TIDYING,TERMINATED 之一,且worker并没有被干扰中断 // 当前线程池的运行状态是 STOP,TIDYING,TERMINATED 之一,调用Thread.interrupted()中断当前线程后,worker没有被干扰中断 // 满足以上两者之一,中断worker wt.interrupt(); } try { // beforeExecute方法是ThreadPoolExecutor类的一个方法,没有具体实现,用户可以根据 beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { // afterExecute方法是ThreadPoolExecutor类的一个方法,没有具体实现,用户可以根据 afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
过程简述:
- 取出worker对象中的任务,同时把worker对象的成员变量firstTask置为null
- 检查任务对象task,如果null==task,尝试去任务队列获取任务
- 直接执行任务对象task的run方法,执行任务
- 继续尝试从任务队列获取任务
- 重复执行步骤3,4,直至从任务队列中获取到的任务为null
方法runWorker()中,任务的来源有两处,参数worker对象,以及任务队列,下面,让我们来看看线程是如何从任务队列来获取任务的。
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 当前线程池的运行状态是SHUTDOWN,任务队列已空 // 当前线程池的运行状态是STOP,TIDYING,TERMINATED之一 // 满足以上两点之一,调用unsafe CAS操作,使得worker数量-1 decrementWorkerCount(); // 最后直接返回null return null; } // 从任务队列中获取任务时,是否需要考虑线程的存活时间 // 如果 timed=true,那么就需要在线程的存活期到期之前取到任务队列中的任务 boolean timed; // Are workers subject to culling? for (;;) { // worker数量 int wc = workerCountOf(c); // allowCoreThreadTimeOut=true,即核心线程亦设置了存活时间 // 当前线程数大于核心线程数,即存在非核心线程 // 满足以上两点之一,timed为true; timed = allowCoreThreadTimeOut || wc > corePoolSize; if (wc <= maximumPoolSize && ! (timedOut && timed)) { break; } // 能执行到此,说明当前线程有存活时间限制,而且在限制的时间之内,没有从任务队列中获取到任务,销毁当前worker,worker数-1 if (compareAndDecrementWorkerCount(c)) { return null; } c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) { continue retry; } else { // CAS failed due to workerCount change; retry inner loop } } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); // timed==false,调用workQueue.take(),如果没有立即拿到任务,线程会被阻塞在这,直到从任务队列拿到任务(不为null) // timed==true,调用workQueue.poll(),如果在限定时间内没有取到任务,执行timedOut = true; if (r != null) return r; // 没有在线程的存活期到期之前从任务队列取到任务,poll方法返回null timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }