线程池
线程池
对线程统一分配、调优和监控
优点:
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
Java1.5中引入的 Executor 框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。
public class ExecutorCase{
// 初始化一个包含10个线程的线程池
private static Executor executor = Executors.newFixedThreadPool(10);
public static void main(String [] args){
for(int i=0;i<20;i++){
// 提交20个任务,每个任务打印当前线程名
executor.execute(new Task());
}
}
static class Task implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
}
核心参数
corePoolSize
线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的 prestartAllCoreThreads() 方法,线程池会提前创建并启动所有核心线程。
maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
keepAliveTime
线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize(产生阻塞)时才有用;
unit
keepAliveTime的单位;
workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在 JDK 中提供了如下阻塞队列:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
- LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
- SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
- priorityBlockingQuene:具有优先级的无界阻塞队列;
threadFactory
创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。
handler
排队策略和拒绝策略
排队策略:
1. 直接提交。直接提交策略表示线程池不对任务进行缓存。新进任务直接提交给线程池,当线程池中没有空闲线程时,创建一个新的线程处理此任务。这种策略需要线程池具有无限增长的可能性。实现为:SynchronousQueue
2. 有界队列。当线程池中线程达到corePoolSize时,新进任务被放在队列里排队等待处理。有界队列(如ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
3. 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
拒绝策略:
线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
- AbortPolicy:直接抛出异常,丢弃任务,默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务(可能造成当前线程阻塞);
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务(不抛异常);
- DiscardPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
Executors
Exectors工厂类提供了线程池的初始化接口,主要有如下几种:
-
newFixedThreadPool
初始化一个指定线程数的线程池,其中 corePoolSize == maximumPoolSize ,使用 LinkedBlockingQuene 作为阻塞队列,不过当线程池没有可执行任务时,不会释放线程。
-
newCachedThreadPool
- 初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用 SynchronousQueue 作为阻塞队列;
- 和 newFixedThreadPool 创建的线程池不同,newCachedThreadPool 在没有任务执行时,当线程的空闲时间超过 keepAliveTime ,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
所以,使用该线程池时,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。
-
newSingleThreadExecutor
初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列。
-
newScheduledThreadPool
初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。
实现原理
除了 newScheduledThreadPool 的内部实现特殊一点之外,其它几个线程池都是基于**ThreadPoolExecutor **类实现的。
线程池内部状态
/**
* java.lang.concurrent.ThreadPoolExecutor
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //32 - 3
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
//111 29*0 remain
private static final int RUNNING = -1 << COUNT_BITS;
//000 29*0 remain
private static final int SHUTDOWN = 0 << COUNT_BITS;
//001 29*0 remain
private static final int STOP = 1 << COUNT_BITS;
//010 29*0 remain
private static final int TIDYING = 2 << COUNT_BITS;
//011 29*0 remain
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; }
其中AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:
1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
3、STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
4、TIDYING : 2 << COUNT_BITS,即高3位为010;
5、TERMINATED: 3 << COUNT_BITS,即高3位为011;
任务提交
线程池框架提供了两种方式提交任务,根据不同的业务需求选择不同的方式。
-
Executor.execute()
通过Executor.execute()方法提交的任务,必须实现Runnable接口,该方式提交的任务不能获取返回值,因此无法判断任务是否执行成功。
-
ExecutorService.submit()
通过ExecutorService.submit()方法提交的任务,可以获取任务执行完的返回值。
任务执行
当向线程池中提交一个任务,线程池会如何处理该任务?
-
execute实现
/** * 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}. * * @param command the task to execute * @throws RejectedExecutionException at discretion of * {@code RejectedExecutionHandler}, if the task * cannot be accepted for execution * @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. * 1. 如果当前running线程数小于corePoolSize,则以 command 作为第一个任务启动一个新的线程 * 在执行addWorker()前,要检查线程池状态是否为RUNNING和当前线程池活动的线程数量, * 通过返回 false 来避免在不该添加线程时添加而产生的警告 * 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. * 2. 如果一个任务成功进入了阻塞队列,那么我们仍然需要二次检查是否应该添加一个线程 * (因为上次检查后会有线程died)或者进入本方法之后线程池关闭了。所以重新检查线程池状态: * 1.如果线程池停止了是否有回滚入队操作的必要, * 2.或者当没有活动线程的时候是否需要启动一个新的线程。 * * 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. * 3. 如果任务无法进入阻塞队列(阻塞队列已满),我们会试图创建一个新的线程来执行任务。 * (workerCountOf(c) > corePoolSize && workerCountOf(c) < maximumPoolSize) * 如果创建新线程失败了,我们就知道线程池已经关闭了,或者线程数已经饱和了 * 所以我们就拒绝这个任务(详见*拒绝策略) */ int c = ctl.get(); if (workerCountOf(c) < corePoolSize) {//活动线程数小于核心线程数 //创建新的线程执行当前任务,这个线程会用死循环监听 workQueue if (addWorker(command, true)) //true 代表未达到 corePoolSize return; c = ctl.get(); } //创建线程不成功,或者已经达到 corePoolSize ,把任务丢进阻塞队列 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) //从阻塞队列移除任务 reject(command); else if (workerCountOf(recheck) == 0)//入队后第二次检查发现没有活动线程 addWorker(null, false); //试图创建空线程去监听 workQueue } //无法进入阻塞队列(已满),试图创建新线程(false代表当前线程数超过了 corePoolSize) else if (!addWorker(command, false)) reject(command); //创建不成功就拒绝当前任务 }
具体的执行流程如下:
1、workerCountOf方法根据ctl的低 29 位,得到线程池的当前线程数,如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务;否则执行步骤(2);
2、如果线程池处于RUNNING状态,且把提交的任务放入阻塞队列中,放入成功则执行步骤(3),否则执行步骤(4);
3、再次检查线程池的状态,如果线程池没有RUNNING,且成功从阻塞队列中移除任务,然后执行reject方法拒绝任务。或者线程池RUNNING但第二次检查发现没有活动线程,则试图创建空线程;
4、无法进入阻塞队列时(已满),尝试执行addWorker方法创建新的线程执行任务(此时workerCountOf© > corePoolSize,所以addWorker方法的第二个参数为false),如果addWoker执行失败,则执行reject方法拒绝任务;
总结:已创建的线程如果没有关闭,当前没有执行任务时,只会从阻塞队列里拿任务来执行(newFixedThreadPool的策略是先创建出corePoolSize数目的线程)
-
addWorker实现
从方法execute的实现可以看出:addWorker主要负责创建新的线程并执行任务,代码实现如下:
/** * 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. * * @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. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && //STOP,TIDYING,TERMINATED 返回 false firstTask == null && //SHUTDOWN 状态新来的任务不为null,返回false ! workQueue.isEmpty())) //SHUTDOWN,任务为null,阻塞队列空,返回false return false; /* * 上段代码 拒绝 STOP,TIDYING,TERMINATED状态的全部新建线程请求, * SHUTDOWN 状态拒绝新任务 * 放通 RUNNING 状态全部新建线程请求 * SHUTDOWN 状态,阻塞队列有任务(全部线程都在工作)新建空线程请求 */ for (;;) { int wc = workerCountOf(c); //当前活动线程数 if (wc >= CAPACITY || //超过了最大允许线程数 或 达到了规定最大线程数 wc >= (core ? corePoolSize : maximumPoolSize)) return false; 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 } }
这只是addWoker方法实现的前半部分:
1、判断线程池的状态,如果线程池的状态值大于SHUTDOWN(RUNNING状态的前三位都是1,但是第一位是符号位,代表是个负数,所以它小于SHUTDOWN),则不处理提交的任务,直接返回false;
2、通过参数core判断当前需要创建的线程是否为核心线程,如果core为true,且当前线程数小于corePoolSize,如果core为false,则则跳出循环,开始创建新的线程,具体实现如下:
boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); //调用内部类 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)) { //RUNNING状态或SHUTDOWN状态添加 null 任务 if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); //private final HashSet<Worker> workers = new HashSet<Worker>(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); // 解锁 } if (workerAdded) { // 新建 Worker 成功后启动 Worker 线程 t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); // worker启动失败, } return workerStarted; }
线程池的工作线程通过ThreadPoolExecutor类的内部类Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并启动Woker中的线程,其中Worker类设计如下:
1、继承了AQS类,可以方便的实现工作线程的中止操作;
2、实现了Runnable接口,可以将自身作为一个任务在工作线程中执行;
3、当前提交的任务firstTask作为参数传入Worker的构造方法;
/** * 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). */ 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; /** 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) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } /** Delegates main run loop to outer runWorker */ public void run() { runWorker(this); } // Lock methods // // The value 0 represents the unlocked state. // The value 1 represents the locked state. 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) { } } } }
从Woker类的构造方法实现可以发现:线程工厂在创建线程thread时,将Woker实例本身this作为参数传入,当执行start方法启动线程thread时,本质是执行了Worker的runWorker方法。
ThreadPoolExecutor.addWorkerFailed() 方法
/** * Rolls back the worker thread creation. * - removes worker from workers, if present * - decrements worker count * - rechecks for termination, in case the existence of this * worker was holding up termination * * 回滚 线程的创建 * - 从 workers 中删除存在的 worker * - 减少 worker 的数量 * - 重新检查线程池是否已经 TERMINATED,以防这个 worker 的存在阻止 TERMINATE */ private void addWorkerFailed(Worker w) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (w != null) workers.remove(w); decrementWorkerCount(); //确认后 减少一个线程数 tryTerminate(); //停掉线程池 } finally { mainLock.unlock(); } }
ThreadPoolExecutor.tryTerminate() 方法
/** * Transitions to TERMINATED state if either (SHUTDOWN and pool * and queue empty) or (STOP and pool empty). If otherwise * eligible to terminate but workerCount is nonzero, interrupts an * idle worker to ensure that shutdown signals propagate. This * method must be called following any action that might make * termination possible -- reducing worker count or removing tasks * from the queue during shutdown. The method is non-private to * allow access from ScheduledThreadPoolExecutor. * 如果(SHUTDOWN,池和队列为空)或(STOP和池为空),则转换为TERMINATED状态。 * 如果 具备终止条件但 workerCount 非零,则中断空闲 worker 以确保‘关闭信号’的传播。 * 必须在可能使线程池终止的任何操作之后调用此方法 * 比如 :在 SHUTDOWN 期间减少 worker 数量或从阻塞队列中删除任务。 * 该方法是非私有的,以允许从ScheduledThreadPoolExecutor访问。 */ final void tryTerminate() { for (;;) { int c = ctl.get(); if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; if (workerCountOf(c) != 0) { // Eligible to terminate interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { try { terminated(); } finally { ctl.set(ctlOf(TERMINATED, 0)); termination.signalAll(); } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }
-
runWorker实现
/** * Main worker run loop. Repeatedly gets tasks from queue and * executes them, while coping with a number of issues: * 主 worker 一直在循环,在处理以下问题时从阻塞队列中重复获取任务,执行任务: * 1. We may start out with an initial task, in which case we * don't need to get the first one. Otherwise, as long as pool is * running, we get tasks from getTask. If it returns null then the * worker exits due to changed pool state or configuration * parameters. Other exits result from exception throws in * external code, in which case completedAbruptly holds, which * usually leads processWorkerExit to replace this thread. * 1. 我们执行初始化任务时,不需要获得第一个任务,否则,只要pool正在运行,我们就会从getTask获取任务。如果它返回null,则由于更改 池的状态或配置参数 而退出 worker。 其他退出是由外部代码中的异常抛出引起的,在这种情况下completedAbruptly持有,这通常导致processWorkerExit替换此线程。 * 2. Before running any task, the lock is acquired to prevent * other pool interrupts while the task is executing, and then we * ensure that unless pool is stopping, this thread does not have * its interrupt set. * 2. 在运行任何任务之前,获取锁以防止在执行任务时其他池产生中断,然后我们确保除非池停止,否则此线程没有设置其中断。 * 3. Each task run is preceded by a call to beforeExecute, which * might throw an exception, in which case we cause thread to die * (breaking loop with completedAbruptly true) without processing * the task. * 3. 每个任务运行之前都会调用 beforeExecute,这可能会抛出异常,在这种情况下,我们会导致线程死亡(使 completedAbruptly 为true跳出循环)而不处理任务。 * 4. Assuming beforeExecute completes normally, we run the task, * gathering any of its thrown exceptions to send to afterExecute. * We separately handle RuntimeException, Error (both of which the * specs guarantee that we trap) and arbitrary Throwables. * Because we cannot rethrow Throwables within Runnable.run, we * wrap them within Errors on the way out (to the thread's * UncaughtExceptionHandler). Any thrown exception also * conservatively causes thread to die. * 4. 假设 beforeExecute 正常完成,我们运行任务,收集任何抛出的异常以发送到 afterExecute。 我们分别处理 RuntimeException,Error(这样保证我们不陷入陷阱)和任意 Throwables。 因为我们不能在Runnable.run 方法中重新抛出Throwables,所以我们将它们包含在出错的Errors中(在线程的UncaughtExceptionHandler 中)。 应该谨慎任何抛出的异常,它们会导致线程死亡。 * 5. After task.run completes, we call afterExecute, which may * also throw an exception, which will also cause thread to * die. According to JLS Sec 14.20, this exception is the one that * will be in effect even if task.run throws. * 5. 在 task.run 方法完成之后,我们调用afterExecute,这也可能引发异常,这也会导致线程死亡。 根据JLS Sec 14.20,即使task.run抛出,该异常也将生效。 * The net effect of the exception mechanics is that afterExecute * and the thread's UncaughtExceptionHandler have as accurate * information as we can provide about any problems encountered by * user code. * * @param w the worker */ final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { //通过 getTask 方法从阻塞队列中获取等待的任务 //如果队列中没有任务,getTask 方法会被阻塞并挂起,不会占用cpu资源; while (task != null || (task = getTask()) != null) { 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()) wt.interrupt(); try { 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(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
runWorker方法是线程池的核心:
1、线程启动之后,通过 unlock 方法释放锁,设置 AQS (AbstractQueuedSynchronizer)的 state 为 0,表示运行中断;
2、获取第一个任务 firstTask,执行任务的run方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁;
3、在执行任务的前后,可以根据业务场景自定义 beforeExecute 和 afterExecute 方法;
4、firstTask 执行完成之后,通过 getTask 方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask 方法会被阻塞并挂起,不会占用cpu资源;
-
getTask实现
/** * Performs blocking or timed wait for a task, depending on * current configuration settings, or returns null if this worker * must exit because of any of: * 1. There are more than maximumPoolSize workers (due to * a call to setMaximumPoolSize). * 2. The pool is stopped. * 3. The pool is shutdown and the queue is empty. * 4. This worker timed out waiting for a task, and timed-out * workers are subject to termination (that is, * {@code allowCoreThreadTimeOut || workerCount > corePoolSize}) * both before and after the timed wait, and if the queue is * non-empty, this worker is not the last thread in the pool. * * @return task, or null if the worker must exit, in which case * workerCount is decremented */ 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. // STOP 状态不处理任务,workQueue 为空也直接返回 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount();//将 workerCount - 1 return null; } int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { // 有超时时间就按超时时间调用,没有就直接出队一个任务 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; // workQueue.poll 因超时而返回 null } catch (InterruptedException retry) { timedOut = false; } } }
整个 getTask 操作在 自旋 下完成:
1、workQueue.take:如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行;
2、workQueue.poll:如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;
所以,线程池中实现的线程可以一直执行由用户提交的任务。
-
Future和Callable实现(应用)
public class ExecutorCase { private static ExecutorService executor = Executors.newFixedThreadPool(10); public static void main(String[] args) { // Future 负责获取结果 Future<String> future = executor.submit(new Task()); System.out.println("do other things"); try { // 阻塞 main 线程,等待线程池返回 String result = future.get(); System.out.println(result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } static class Task implements Callable<String>{ // Callable 负责产生结果 @Override public String call() throws Exception { try { TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e) { e.printStackTrace(); } return "this is future case"; } } }
运行结果:
->do other things ->this is future case
在实际业务场景中,Future 和 Callable 基本是成对出现的,Callable 负责产生结果,Future 负责获取结果。
1、Callable 接口类似于 Runnable,只是 Runnable 没有返回值。
2、Callable 任务除了返回正常结果之外,如果发生异常,该异常也会被返回,即 Future 可以拿到异步执行任务各种结果;
3、Future.get 方法会导致主线程阻塞,直到Callable任务执行完成;
-
submit实现
/** * @throws RejectedExecutionException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); // return new FutureTask<T>(callable); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
通过 submit 方法提交的 Callable 任务会被封装成了一个 FutureTask 对象,提交后返回了任务对象。
FutureTask
/** * The run state of this task, initially NEW. The run state * transitions to a terminal state only in methods set, * setException, and cancel. During completion, state may take on * transient values of COMPLETING (while outcome is being set) or * INTERRUPTING (only while interrupting the runner to satisfy a * cancel(true)). Transitions from these intermediate to final * states use cheaper ordered/lazy writes because values are unique * and cannot be further modified. * * Possible state transitions: * NEW -> COMPLETING -> NORMAL * NEW -> COMPLETING -> EXCEPTIONAL * NEW -> CANCELLED * NEW -> INTERRUPTING -> INTERRUPTED */ private volatile int state; private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; private static final int INTERRUPTING = 5; private static final int INTERRUPTED = 6;
1、FutureTask 在不同阶段拥有不同的状态 state,初始化为NEW;
2、FutureTask 类实现了 Runnable 接口,这样就可以通过 Executor.execute 方法提交 FutureTask 到线程池中等待被执行,最终执行的是 FutureTask 的 run 方法;
-
FutureTask.get实现
/** * @throws CancellationException {@inheritDoc} */ public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
内部通过 awaitDone 方法对主线程进行阻塞,具体实现如下:
/** * Awaits completion or aborts on interrupt or timeout. * * @param timed true if use timed waits * @param nanos time to wait, if timed * @return state upon completion */ private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) q = new WaitNode(); else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); } }
1、如果主线程被中断,则抛出中断异常;
2、判断 FutureTask 当前的 state ,如果大于 COMPLETING ,说明任务已经执行完成,则直接返回;
3、如果当前 state 等于 COMPLETING ,说明任务已经执行完,这时主线程只需通过 yield 方法让出 cpu 资源,等待 state 变成 NORMAL ;
4、通过 WaitNode 类封装当前线程,并通过 UNSAFE 添加到 waiters 链表;
5、最终通过 LockSupport 的 park 或 parkNanos 挂起线程;
-
FutureTask.run实现
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
FutureTask.run 方法是在线程池中被执行的,而非主线程
1、通过执行 Callable 任务的 call 方法;
2、如果 call 执行成功,则通过 set 方法保存结果;
3、如果 call 执行有异常,则通过 setException 保存异常;
-
set
/** * Sets the result of this future to the given value unless * this future has already been set or has been cancelled. * * <p>This method is invoked internally by the {@link #run} method * upon successful completion of the computation. * * @param v the value */ protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } }
-
setException
/** * Causes this future to report an {@link ExecutionException} * with the given throwable as its cause, unless this future has * already been set or has been cancelled. * * <p>This method is invoked internally by the {@link #run} method * upon failure of the computation. * * @param t the cause of failure */ protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } }
set 和 setException 方法中,都会通过 UNSAFE修改 FutureTask 的状态,并执行 finishCompletion 方法通知主线程任务已经执行完成;
-
finishCompletion
/** * Removes and signals all waiting threads, invokes done(), and * nulls out callable. */ private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; // unlink to help gc q = next; } break; } } done(); callable = null; // to reduce footprint }
1、执行 FutureTask 类的 get 方法时,会把主线程封装成 WaitNode 节点并保存在 waiters 链表中;
2、FutureTask 任务执行完成后,通过 UNSAFE 设置 waiters 的值,并通过 LockSupport 类 unpark 方法唤醒主线程;