java 线程池
图左边是线程池的类结构,右边是放入线程池执行的任务类结构
ExecutorService定义了线程池的基本的方法,AbstractExecutorService是个抽象类,实现了ExecutorService的部分,主要是实现了几个submit()方法,并且提供方法将提交进来的任务封装成FutureTask,ThreadPoolExecutor则实现线程池的管理.
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Object> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
调用的是ThreadPoolExecutor类实现的execute(Runnable)方法:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {//关键点1 if (runState == RUNNING && workQueue.offer(command)) {//关键点2 if (runState != RUNNING || poolSize == 0)//关键点3 ensureQueuedTaskHandled(command); } else if (!addIfUnderMaximumPoolSize(command))关键点4 reject(command); // is shutdown or saturated } }
ThreadPoolExecutor里面有几个size:poolSize 当前线程池的大小,corePoolSize 核心线程池大小,maximumPoolSize最大线程池大小。根据这几个size线程池执行任务的方式也会不同。
关键点1:当poolSize大于coreSize的时候执行关键点2,也就是进入等待执行的队列;否则执行addIfUnderMaximumPoolSize()方法,该方法创建一个线程,这个线程被加入到工作线程集当中,并且第一个要执行的就是当前这个任务。
关键点2:poolSize小于coreSize时将任务加入到等待队列,如果成功执行关键点3,如果ThreadPoolExecutor的状态不是runnable或者等待队列加入失败的话,执行关键点4
关键点3:此时可能ThreadPoolExecutor的状态不是runnable或者poolSize为0,可能是执行shutDown()引起的,这个需要特殊处理
关键点4:此时poolSize大于coreSize,然后等待队列满了,需要直接创建一个线程执行任务,并把线程放入到工作线程集中。如果前面执行不成功,那么就需要执行相应的饱和策略了
总结:线程池包含三部分:coreSize大小的核心线程池,等待队列,以及扩展线程池,核心线程池和扩展线程池其实是在同一个工作线程集当中
工作线程
工作线程集中每一个对象用内部类Worker表示,实现了Runnable接口,里面引用了线程:
private final class Worker implements Runnable { /** * The runLock is acquired and released surrounding each task * execution. It mainly protects against interrupts that are * intended to cancel the worker thread from instead * interrupting the task being run. */ private final ReentrantLock runLock = new ReentrantLock(); /** * Initial task to run before entering run loop. Possibly null. */ private Runnable firstTask; /** * Per thread completed task counter; accumulated * into completedTaskCount upon termination. */ volatile long completedTasks; /** * Thread this worker is running in. Acts as a final field, * but cannot be set until thread is created. */ Thread thread; Worker(Runnable firstTask) { //第一个任务 this.firstTask = firstTask; } boolean isActive() { return runLock.isLocked(); } /** * Interrupts thread if not running a task. */ void interruptIfIdle() { final ReentrantLock runLock = this.runLock; if (runLock.tryLock()) { try { if (thread != Thread.currentThread()) thread.interrupt(); } finally { runLock.unlock(); } } } /** * Interrupts thread even if running a task. */ void interruptNow() { thread.interrupt(); } /** * Runs a single task between before/after methods. */ private void runTask(Runnable task) { final ReentrantLock runLock = this.runLock; runLock.lock(); try { /* * Ensure that unless pool is stopping, this thread * does not have its interrupt set. This requires a * double-check of state in case the interrupt was * cleared concurrently with a shutdownNow -- if so, * the interrupt is re-enabled. */ //双重检测,对shutdown()执行后而且执行了interruptIfIdle()方法(在getTask()中)这段代码会用到 if (runState < STOP && Thread.interrupted() && runState >= STOP) thread.interrupt(); /* * Track execution state to ensure that afterExecute * is called only if task completed or threw * exception. Otherwise, the caught runtime exception * will have been thrown by afterExecute itself, in * which case we don't want to call it again. */ boolean ran = false; //执行前的hook beforeExecute(thread, task); try { //FutureTask的run()方法 task.run(); ran = true; //执行后的hook afterExecute(task, null); ++completedTasks; } catch (RuntimeException ex) { if (!ran) afterExecute(task, ex); throw ex; } } finally { runLock.unlock(); } } /** * Main run loop * 由于本runnable在创建线程时是线程构造函数的参数, * 所以线程运行最终会运行下面的run方法。 * 而且属性thread的值就是创建的线程 */ public void run() { try { Runnable task = firstTask; firstTask = null; //循环中运行runTask()方法,firstTask不为空,就先运行firstTask //之后运行getTask()方法,该方法就是从等待队列里面获取任务或者阻塞等待任务。 //由于得到的task可能为空,所以while循环可能跳出 while (task != null || (task = getTask()) != null) { //执行任务 runTask(task); task = null; } } finally { workerDone(this); } } }
工作线程执行第一个任务,然后加入到线程池,不断执行上面的run()方法,基本就是:等待队列取任务-->执行-->取任务-->执行的循环,中间需要处理外部执行shutdown()后的流程,需要动态的增大减小工作线程池,核心线程池,扩展线程池。getTask()方法里面有做这些:
Runnable getTask() { for (;;) { try { int state = runState; //线程池状态为STOP或者TERMINATED的话,就不执行队列里面的任务了 if (state > SHUTDOWN) return null; Runnable r; if (state == SHUTDOWN) // Help drain queue //在SHUTDOWN的话还是需要执行任务的 r = workQueue.poll(); //如果poolSize>corePoolSize 说明线程池大于核心线程池,那么队列 //可能不会有任务,allowCoreThreadTimeOut为true说明核心线程池 //线程timeout以后可以被回收,如果是上面二个条件之一的话,使用poll()方法 //可能超时后返回的就是空的任务 else if (poolSize > corePoolSize || allowCoreThreadTimeOut) r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); else //上面两条不符合,那么队列有任务的概率大,这个会阻塞知道有任务(反正核心线程池不会被回收的) r = workQueue.take(); if (r != null) return r; //上面可能返回为空,所以能运行到这里 //运行workerCanExit()方法,如果返回true,那么检测如果 //线程池状态为STOP或者TERMINATED的话,就开始中断空闲的线程 if (workerCanExit()) { if (runState >= SHUTDOWN) // Wake up others interruptIdleWorkers(); return null; } // Else retry } catch (InterruptedException ie) { // On interruption, re-check runState } } } private boolean workerCanExit() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); boolean canExit; try { //线程池状态为STOP或者TERMINATED或者等待队列为空或者核心线程可以回收加上工作 //线程池大于核心线程池 canExit = runState >= STOP || workQueue.isEmpty() || (allowCoreThreadTimeOut && poolSize > Math.max(1, corePoolSize)); } finally { mainLock.unlock(); } return canExit; } //getTask()由于各种调节功能使得返回的任务可能为空,run()方法的workerDone()就有机会执行: void workerDone(Worker w) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //更新总的完成次数 completedTaskCount += w.completedTasks; //执行到这里说明核心线程池可以回收而且返回了的任务为空 //认为核心线程池太大,进行回收 workers.remove(w); if (--poolSize == 0) tryTerminate(); } finally { mainLock.unlock(); } } // 在Worker的runTask()方法中最主要还是执行FutureTask的run方法: public void run() { sync.innerRun(); } private static final int RUNNING = 1; /** State value representing that task ran */ private static final int RAN = 2; /** State value representing that task was cancelled */ private static final int CANCELLED = 4; void innerRun() { //初始状态变了,直接返回 if (!compareAndSetState(0, RUNNING)) return; try { runner = Thread.currentThread(); //如果不是RUNNING,那么肯定是RAN或者CANCELLED //这两个状态都可以释放锁了 if (getState() == RUNNING) // recheck after setting thread //设置执行的结果 innerSet(callable.call()); else //这里会执行到FutureTask内部的实现的tryReleaseShared方法 releaseShared(0); // cancel } catch (Throwable ex) { innerSetException(ex); } } void innerSet(V v) { //循环只到下面一项产生 for (;;) { int s = getState(); //另外的线程已经执行了 直接返回 if (s == RAN) return; //任务取消了,释放锁 if (s == CANCELLED) { // aggressively release to set runner to null, // in case we are racing with a cancel request // that will try to interrupt runner releaseShared(0); return; } //设置完成标志,然后设置result,释放锁,唤醒阻塞在FutureTask.get()上面的线程 if (compareAndSetState(s, RAN)) { result = v; releaseShared(0); //hook done(); return; } } } protected boolean tryReleaseShared(int ignore) { //线程清空 runner = null; return true; }
说到底FutureTask还是用AQS实现的。
当maximumPoolSize表示的最大线程池满了以后,加入的任务会被拒绝,JDK有四种拒绝的策略:
AbortPolicy:抛出异常
CallerRunsPolicy:当前的线程直接的执行任务
DiscardOldestPolicy:舍去最老的任务,然后线程池执行最新的任务
DiscardPolicy:直接拒绝任务
总结:
线程池设计的初衷还是减少了每个任务调用的开销,可以在执行大量异步任务时提高性能,并且还可以管理资源
提高性能方面主要是去除了大部分任务调用时线程的创建和销毁的开销
资源管理方面:有核心线程池,等待队列,扩展线程池等方面,对于不同的方面有不同的策略,并且存在一定的
动态调节线程池的能力,对线程池超负荷时也有一些拒绝策略