Java并发包中的线程池ThreadPoolExecutor
线程池主要解决两个问题:①线程池中线程是可复用的,节省了创建销毁线程的开销;②线程池提供资源限制和管理手段,如线程的个数,动态新增线程
一、ThreadPoolExecutor
1.变量
//高3位表示线程池状态,低29位表示线程个数 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 bit //运行期:接受新任务并且处理阻塞队列里的任务 private static final int RUNNING = -1 << COUNT_BITS; //拒绝新任务但是处理阻塞队列里的任务 private static final int SHUTDOWN = 0 << COUNT_BITS; //拒绝新任务并且抛弃阻塞队列里的任务,同时会中断正在处理的任务 private static final int STOP = 1 << COUNT_BITS; //所有任务都执行完后当前线程池活动线程数为0,将要调用terminated方法 private static final int TIDYING = 2 << COUNT_BITS; //终止状态 private static final int TERMINATED = 3 << COUNT_BITS; //阻塞队列 private final BlockingQueue<Runnable> workQueue; /** * 独占锁 */ private final ReentrantLock mainLock = new ReentrantLock(); /** * 工作线程集合,访问时需要加锁mainLock */ private final HashSet<Worker> workers = new HashSet<Worker>(); /** * 条件信号量 */ private final Condition termination = mainLock.newCondition(); /** * 最大线程池大小,访问时需要加锁 */ private int largestPoolSize; /** * 已完成任务的计数器,仅在工作线程执行完后更新,访问时需加锁mainLock */ private long completedTaskCount; /* * 所有用户控制变量设置为volatile */ /** * 线程工厂 */ private volatile ThreadFactory threadFactory; /** * shutdown或者线程池饱和不能再处理任务时的拒绝处理程序 */ private volatile RejectedExecutionHandler handler; /** * 线程存活时间单位是纳秒,①核心线程设置allowCoreThreadTimeOut=true时核心线程空闲时的存活时间②除核心线程外的工作线程空闲时存活时间 */ private volatile long keepAliveTime; /** * 线程池核心线程数属性,为false时,核心线程空闲时一直保持活动状态,为true,核心线程空闲时keepAliveTime时间内保持活动状态。 */ private volatile boolean allowCoreThreadTimeOut; /** * 线程池核心线程数,运行时保持的最小线程数,设置allowCoreThreadTimeOut时,最小值为0 */ private volatile int corePoolSize; /** * 线程池最大线程数 */ private volatile int maximumPoolSize; /** * 默认拒绝处理任务的处理程序 */ private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); /** * */ private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread"); /* 执行finalizer时的Context,或为空*/ private final AccessControlContext acc;
2.构造方法,初始化用户控制变量
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { //默认线程工厂Executors.defaultThreadFactory 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; }
3.常用方法
1)void execute(Runnable command):
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); //当前线程池中线程个数小于设置的核心线程数,开启新线程运行 if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } //当前线程池中线程个数大于或等于设置的核心线程数,且线程池处于RUNNING状态,任务入阻塞队列 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) //重新检查线程池状态,若不处于RUNNING状态,删除任务并执行拒绝策略RejectedExecutionHandler reject(command); else if (workerCountOf(recheck) == 0) //线程处于RUNNING状态且线程个数为0,添加一个线程 addWorker(null, false); } //如果队列满,则新增线程,新增失败则执行拒绝策略 else if (!addWorker(command, false)) reject(command); } private boolean addWorker(Runnable firstTask, boolean core) { retry: //CAS自旋实现工作线程数增加workerCount++, for (;;) { int c = ctl.get(); int rs = runStateOf(c); // 仅在必要的时候检查队列是否为空 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); //线程数达到最大 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //CAS自旋实现workerCount++ if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl //线程池状态改变了,自旋重试 if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; //创建线程 执行thread(worker),所以线程池中的线程执行的是worker的run方法 Worker w = null; try { //通过Worker的构造方法,初始化Worker时会初始化new Thread(worker),即下面thread.start()中run方法时worker的run方法 w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get()); //线程池状态检查 if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); //入workerSet,相应的线程销毁时,会出workers workers.add(w); int s = workers.size(); if (s > largestPoolSize) //当前工作线程数 largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { //workeradded成功即worker(thread+command)新建成功,thread.start() t.start(); workerStarted = true; } } } finally { if (! workerStarted) //addWorker失败 还原状态 workCount--、works.remove(w)、还会尝试终止当前线程 addWorkerFailed(w); } return workerStarted; }
通过execute方法发现
①当工作线程数小于核心线程数时,执行execute方法会使线程池新建一个Worker(新建一个线程实例+绑定当前任务command)实例,然后开始线程,new Thread(worker).start()
②当工作线程数等于大于核心线程数时,当前任务command入阻塞队列workQueue
研究线程池时怎么实现线程复用的?关键就是研究worker.run()
2).内部静态类Worker及实现线程复用的worker.run()
①Worker是一个继承AQS的一个不可重入独占锁
②从实例关系上:Worker与thread是一对一的关系,并且会保存创建thread执行的第一个任务firstTask和记录thread已经完成的任务数completedTasks
private final class Worker extends AbstractQueuedSynchronizer implements Runnable { private static final long serialVersionUID = 6138294804551838833L; /** 工作线程 */ final Thread thread; /** 需要创建线程时绑定command */ Runnable firstTask; /** 当前线程完成的任务command数 */ volatile long completedTasks; /** * 用线程工厂创建线程实例 new Thread(Worker) */ Worker(Runnable firstTask) { setState(-1); // 在调用runWorker前禁止中断,下面的interruptIfStart方法 inhibit interrupts until runWorker this.firstTask = firstTask;//线程的第一个任务command this.thread = getThreadFactory().newThread(this); } /** 线程复用实现 Delegates main run loop to outer runWorker */ public void run() { runWorker(this); } //下面是不可重入独占锁的实现 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) { } } } }
线程复用实质:new thread(worker).start-->worker.run()-->runWorker(worker)
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); //将state设置为0,允许中断 allow interrupts boolean completedAbruptly = true; try { //这里就是线程复用的实质,不停从阻塞队列workerQueue中获取阻塞任务执行 while (task != null || (task = getTask()) != null) { w.lock();//worker不可重入独占锁 // 如果线程池正在停止Stopping,确保线程中断 // 如果没有,确保线程没有被中断 // 第二种情况需要复查处理 // 清除中断时立即停止比赛 if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { //执行任务前操作--空方法预留扩展 beforeExecute(wt, task); Throwable thrown = null; try { //实际的任务运行command.run() 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(task, thrown); } } finally { task = null; //当前线程完成任务数更新++ w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { //执行清理工作, //将当前线程完成任务数累加到总任务数上 //从workerSet中删除当前工作线程 //尝试终止线程池 //如果当前线程个数小于核心个数,创建线程 processWorkerExit(w, completedAbruptly); } } private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // 仅在必要时检查队列是否为空 Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // 线程是否会被销毁的标志 Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //销毁当前线程①线程数大于设定的最大线程数 // ②当大于核心线程小于最大线程数或allowCoreThreadTimeOut == true并且当前线程空闲超过keepAliveTime时 // ③wokerQueue为空 if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) //CAS自旋保证线程安全workerCount-- return null; continue; } try { //配合上面的②,线程空闲时长超keepAliveTime时销毁 //线程数小于核心线程数且allowCoreThreadTimeOut == false,调用take()阻塞出队 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
综上,不妨将其先简化成简单的模型:将线程创建销毁与workerQueue工作队列分开,
设置线程池核心线程数为2,同时来了4个任务command
①4个任务入阻塞队列
②创建线程thread1处理command1,创建线程thread2处理command2,线程数达到最大,其他任务继续阻塞
③thread2处理command2完毕,从队列中取出command3继续处理......结束;销毁线程
实际的线程池ThreadPoolExecutor实际将①②进行了优化设计,
①边创建线程thread,边处理任务command,
②直到线程满后,任务command入工作队列
③已创建的线程从工作队列中获取command运行
3)void shutdown():不接受新任务,工作队列中的任务继续执行
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //权限检查 checkShutdownAccess(); //设置当前线程池为SHUTDOWN状态 advanceRunState(SHUTDOWN); //设置中断标志 interruptIdleWorkers(); onShutdown(); // 空方法ScheduledThreadPoolExecutor的钩子,(设计模式中的模板模式) } finally { mainLock.unlock(); } // tryTerminate(); }
4)List<Runnable> shutdownNow():不接受新任务,丢弃工作队列中的任务,返回值为工作队列
public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); //中断所有线程 interruptWorkers(); //获取工作队列 tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }
5)boolean awaitTermination(long timeout, TimeUnit unit):调用此方法后,当前线程被阻塞,入termination条件阻塞队列,直到线程池状态变为TERMINATED或者超时才返回。
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (;;) { if (runStateAtLeast(ctl.get(), TERMINATED)) return true; if (nanos <= 0) return false; nanos = termination.awaitNanos(nanos); } } finally { mainLock.unlock(); } }
4.总结:
1)巧妙用一个AtomicInteger原子变量记录线程池状态和线程池中的线程个数
2)线程池之间的状态转换(偷的图)
3)抽象了一个Worker类:
①继承AQS的不可重入独占锁
②与thread是一对一关系,并且记录了thread首次任务,完成任务次数
③thread.start()中运行的是worker.run()这也是实现线程获取工作队列元素实现线程复用的方法
4)线程复用的实质:线程延迟处理工作队列中的任务。
参考自《java并发编程之美》