相关文章:
在项目开发过程中,服务端会接收请求并进行处理,当请求数量不大时,我们可以为每个请求都分配一个线程,而当请求数量很大时,如果还是为每个请求都分配一个线程的话,会导致频繁地创建和销毁线程,从而大大降低了系统的效率,这个时候,线程池便派上了用场,通过对线程的重复使用,大大地提高了系统的效率
总的来说,线程池的优点可以归纳为以下几点
-
降低资源消耗
- 通过重复利用已创建的线程降低线程创建和销毁造成的消耗
-
提高响应速度
- 当任务达到时,可以不需要等待新的线程创建就能立即执行
-
提高线程的可管理性
- 线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控
一、内部类解析
-
Worker
// 继承自 AbstractQueuedSynchronizer (AQS),实现了锁控制 private final class Worker extends AbstractQueuedSynchronizer implements Runnable { // Worker 类永远不会被序列化,但提供了一个 serialVersionUID 来避免编译警告 private static final long serialVersionUID = 6138294804551838833L; // Worker 运行所在的工作线程 (由线程工厂创建,如果创建失败,则为 null) final Thread thread; // 工作线程要执行的初始任务,可能为 null Runnable firstTask; // 工作线程完成的任务数量 volatile long completedTasks; // 使用给定的第一个任务和线程工厂创建的线程来构造 Worker Worker(Runnable firstTask) { // 限制线程直到 runWorker() 方法执行之前都不允许被打断 setState(-1); this.firstTask = firstTask; // 使用线程工厂创建线程 this.thread = getThreadFactory().newThread(this); } public void run() { // 调用 runWorker() 方法 runWorker(this); } // 判断线程是否获得了独占锁 0=未获得 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) { // 设置当前拥有独占访问权限的线程 (null) setExclusiveOwnerThread(null); setState(0); return true; } /* * 获取独占锁,其内部会调用 tryAcquire(int unused) 方法来获取独占锁, * 如果获取失败,则会将当前线程和给定模式包裹成一个 Node 节点,将其加入到 CLH 队列尾部 */ public void lock() { acquire(1); } // 尝试获取独占锁 public boolean tryLock() { return tryAcquire(1); } /* * 释放独占锁,其内部会调用 tryRelease(int unused) 方法来释放独占锁 * 如果释放成功,且当前节点存在后继节点,则对其进行唤醒 */ public void unlock() { release(1); } // 判断线程是否获得了独占锁 0=未获得 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 对象,线程池维护的其实就是一组 Worker 对象
-
Worker 类继承自 AQS,并实现了 Runnable 接口,两个关键参数
thread
和firstTask
参数 作用 thread 工作线程,由线程工厂 (ThreadFactory) 创建,用于处理任务 firstTask 工作线程要执行的初始任务,即在构造方法中传入的任务 -
在 Worker 类的构造方法中,会通过
getThreadFactory().newThread(this)
方法来新建一个线程,其中this
表示的是当前对象,也就是 Worker 对象,由于 Worker 本身实现了 Runnable 接口,所以当一个 Worker 对象启动时,会调用自身的 run() 方法 -
Worker 类使用 AQS 来实现独占锁的功能
-
-
CallerRunsPolicy
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
- 线程拒绝策略之一,将任务分配给调用线程来执行
-
AbortPolicy
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); } } }
- 线程拒绝策略之一,丢弃队列中最早的任务,不抛出异常
二、构造方法解析
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
-
核心线程数
-
线程池启动后默认是空的,只有当任务来临时才会创建线程进行处理
-
当核心线程空闲时,会一直等待新的任务,而不会被销毁回收
-
相关方法
方法 作用 prestartCoreThread() 启动一个核心线程,使其等待执行任务,返回 true
此方法会覆盖在执行新任务时,启动核心线程的默认策略
如果所有核心线程均已启动,则调用此方法会返回 falseprestartAllCoreThreads() 启动所有核心线程,使其等待执行任务
此方法会覆盖在执行新任务时,启动核心线程的默认策略
-
-
maximumPoolSize
-
最大线程数
-
当 workQueue 使用无界队列 (如:没有设置 capacity 的 LinkedBlockingQueue) 时,此参数无效
-
-
keepAliveTime
-
非核心线程的最大存活时间
-
当非核心线程空闲时间超过最大存活时间时,会被销毁回收
-
相关方法
方法 作用 allowCoreThreadTimeOut(boolean value) 设置是否允许核心线程超时 allowsCoreThreadTimeOut() 获取核心线程是否可以超时的标识
-
-
unit
-
参数 keepAliveTime 的时间单位,类型为 TimeUnit
-
一共有七种枚举常量,如下所示
常量名 含义 NANOSECONDS 纳秒,等于 $1 * 10^{-9}s$
MICROSECONDS 微秒,等于 $1 * 10^{-6}s$
MILLISECONDS 毫秒,等于 $1 * 10^{-3}s$
SECONDS 秒 MINUTES 分 HOURS 时 DAYS 日
-
-
workQueue
-
工作队列
-
当线程池中线程数量达到核心线程数,且都处于活跃状态时,会将新提交的任务放入队列中,等待空闲的线程来获取处理
-
-
threadFactory
-
线程创建工厂
-
创建线程或线程池时尽量指定有意义的线程名称,方便出错时回溯
-
创建线程池的时候,尽量使用带有 ThreadFactory 的构造函数,并提供自定义的 ThreadFactory 实现或者使用第三方实现
-
-
handler
-
线程拒绝策略
-
一共有四种线程拒绝策略,如下所示
拒绝策略 含义 AbortPolicy 直接丢弃任务,抛出异常 (默认策略) CallerRunsPolicy 将任务分配给调用线程来执行 DiscardPolicy 直接丢弃任务,不抛出异常 DiscardOldestPolicy 丢弃队列中最早的任务,不抛出异常
-
-
三、线程池状态解析
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29位
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 bits
// 线程池运行状态
// 111 + 29个0
private static final int RUNNING = -1 << COUNT_BITS;
// 000 + 29个0
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 001 + 29个0
private static final int STOP = 1 << COUNT_BITS;
// 010 + 29个0
private static final int TIDYING = 2 << COUNT_BITS;
// 011 + 29个0
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; }
// 反向构造 ctl 的值,初始值为 ctlOf(RUNNING, 0)
private static int ctlOf(int rs, int wc) { return rs | wc; }
-
ctl:
-
一个 32 位的 int 值,用于表示线程池的运行状态 (runState) 和线程池中的线程数目 (workerCount)
概念 含义 runState 占据 ctl 的高 3 位,表示线程池的运行状态 workerCount 占据 ctl 的低 29 位,表示线程池中的线程数目
-
-
COUNT_BITS
- 表示 29 位
-
CAPACITY
- 线程池最大容量 (
2^29 - 1
)
- 线程池最大容量 (
-
runStateOf(int c)
private static int runStateOf(int c) { return c & ~CAPACITY; }
-
获取线程池运行状态
-
c 代表 ctl 的值,将其与
~CAPACITY
(111 00000 00000000 00000000 00000000
) 进行 & 操作,c 的低 29 位与~CAPACITY
的低 29 位00000 00000000 00000000 00000000
进行 & 操作,无论 c 的低 29 位为何值,与00000 00000000 00000000 00000000
进行 & 操作后都会变为00000 00000000 00000000 00000000
,也就是舍弃了 c 的低 29 位;而 c 的高 3 位与~CAPACITY
的高 3 位111
进行 & 操作,c 的高 3 位还是会保持原值,这样我们就从 c 中解析出了 runState 的值
-
-
workerCountOf(int c)
private static int workerCountOf(int c) { return c & CAPACITY; }
-
获取线程池中线程数目
-
c 代表 ctl 的值,将其与 CAPACITY 进行 & 操作,也就是与
000 11111 11111111 11111111 11111111
进行 & 操作,c 的高 3 位与 CAPACITY 的高 3 位000
进行 & 操作,无论 c 的高 3 位为何值,与000
进行 & 操作后都会变为000
,也就是舍弃了 c 的高 3 位值;而 c 的低 29 位与 CAPACITY 的低 29 位11111 11111111 11111111 11111111
进行 & 操作,c 的低 29 位还是会保持原值,这样我们就从 c 中解析出了 workerCount 的值 -
线程池状态的常量可以通过值的大小来表示先后关系,如:
rs >= SHUTDOWN
可用于表示线程的状态可能是 SHUTDOWN 或 STOP 或 TIDYING 或 TERMINATED,而不可能是 RUNNING
-
-
ctlOf(int rs, int wc)
private static int ctlOf(int rs, int wc) { return rs | wc; }
-
反向构造 ctl 的值,初始值为
ctlOf(RUNNING, 0)
-
rs 表示线程池的运行状态 (runState),其高 3 位有值,低 29 位全部为 0;wc 表示线程池中的线程数目 (workerCount),其高 3 为全部为 0,低 29 位有值
-
将 rs 与 wc 进行 | 操作,即用 rs 的高 3 位与 wc 的低 29 位填充成一个新的 int 值 (即 ctl 的值)
-
-
线程池有五大状态,分别是:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED
状态 作用 RUNNING (-1) 接受新任务并处理排队任务 SHUTDOWN (0) 不接受新任务,但会处理排队任务
当线程处于 RUNNING 状态时,调用 shutdown() 方法会使线程池进入该状态STOP (1) 不接受新任务,也不处理排队任务,同时会中断正在处理的任务 TIDYING (2) 所有任务都已终止,有效线程数 (workerCount) 为零
当线程池进入该状态时,会调用 terminated() 方法TERMINATED (3) terminated() 方法执行后,线程池会进入该状态
四、方法解析
-
addWorker(Runnable firstTask, boolean core)
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); // 获取线程池运行状态 int rs = runStateOf(c); /* * 此条件可等价地转换为 rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()) * 即可分为以下 3 个条件,只要有一个条件满足,则表示不再添加新的线程来执行任务 * 1、rs >= SHUTDOWN && rs != SHUTDOWN * 线程池状态是 STOP (1)、TIDYING (2)、TERMINATED (3) 的其中一种 * 2、rs >= SHUTDOWN && firstTask != null * 线程池状态不是 RUNNING (-1) 且已经接受了新的任务 * 3、rs >= SHUTDOWN && workQueue.isEmpty() * 线程池状态不是 RUNNING (-1) 且工作队列为空 */ if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { // 获取工作线程数 int wc = workerCountOf(c); /* * 如果 wc >= CAPACITY 或 wc >= corePoolSize / maximumPoolSize,则返回 false * 这里第二个条件中的 core 为 addWorker() 方法的第二个参数 * 1、为 true 时,表示根据 corePoolSize 来进行判断 * 2、为 false 时,表示根据 maximumPoolSize 来进行判断 */ if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; /* * 尝试增加线程池中线程数目, * 如果成功,则跳出外层循环 retry */ if (compareAndIncrementWorkerCount(c)) break retry; // 如果增加线程失败,则重新获取 ctl c = ctl.get(); /* * 重新获取线程池运行状态,并与先前获取的线程池运行状态进行比较, * 如果两者不相等,说明线程池状态已被改变,则返回外层循环继续执行 */ if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } // 标记工作线程是否已启动 boolean workerStarted = false; // 标记工作线程是否被添加 boolean workerAdded = false; Worker w = null; try { // 根据 firstTask 构建 Worker 对象 w = new Worker(firstTask); // 获取 Worker 对象中的工作线程 final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // 获取线程池运行状态 int rs = runStateOf(ctl.get()); /* * 如果 rs < SHUTDOWN 或 (如果 rs == SHUTDOWN 且 firstTask 为 null) * 则向线程池中添加线程来处理任务 * (当线程池状态为 SHUTDOWN 时,虽然不会再接受新任务,但仍然会处理排队任务) */ if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { // 检测工作线程是否处于活动状态 if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); // 如果工作线程不处于活动状态,则向 workers (HashSet) 中添加 Worker workers.add(w); // 获取 workers 的容量 int s = workers.size(); /* * 如果 s > largestPoolSize, * 则将 s 赋值给 largestPoolSize * 1、largestPoolSize * 用于记录线程池中出现过的最大线程数量 */ if (s > largestPoolSize) largestPoolSize = s; // 将工作线程添加标识设为 true workerAdded = true; } } finally { mainLock.unlock(); } /* * 如果工作线程已被添加, * 则启动工作线程,并将工作线程启动标识设为 true */ if (workerAdded) { t.start(); workerStarted = true; } } } finally { // 如果工作线程未被启动,则回滚工作线程的创建 if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
-
addWorker() 方法主要用于在线程池中创建一个新的线程来执行任务,其有两个参数
firstTask
和core
参数 作用 firstTask 工作线程要执行的初始任务,即在构造方法中传入的任务 core 为 true 时,表示在新增线程时,
会判断当前工作线程数 (workerCount) 是否小于核心线程数 (corePoolSize);
为 false 时,表示在新增线程时,
会判断当前工作线程数 (workerCount) 是否小于最大线程数 (maximumPoolSize)
-
-
runWorker(Worker w)
final void runWorker(Worker w) { // 获取当前线程 Thread wt = Thread.currentThread(); // 获取初始任务 Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { // 循环获取任务 while (task != null || (task = getTask()) != null) { w.lock(); /* * 此条件可等价地转换为以下 2 个条件, * 只要有一个条件满足,则设置当前线程为中断状态 * 1、runStateAtLeast(ctl.get(), STOP) && !wt.isInterrupted() * 线程池处于 STOP (1) 或 TIDYING (2) * 或 TERMINATED (3) 状态且当前线程未被中断,则设置当前线程为中断状态 * 2、(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)) && !wt.isInterrupted() * 如果在执行 if 语句期间,执行了 shutdownNow() / shutdownNow() 方法, * 则会将线程池中所有线程设为中断状态,此时调用 Thread.interrupted() 会清除当前线程的中断标识 * 且此时线程池处于 STOP (1) 或 TIDYING (2) 或 TERMINATED (3) 状态 * 且当前线程未被中断 (已被 interrupted() 方法复位),则设置当前线程为中断状态 */ 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; // 统计当前 worker 完成了多少任务 w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { /* * 线程结束时调用,做一些收尾工作 * 如:将 worker 从 workers 中移除、统计任务完成个数等 */ processWorkerExit(w, completedAbruptly); } }
- runWorker() 方法主要用于执行任务
-
getTask()
private Runnable getTask() { // 标记上次从工作队列中获取任务是否超时 boolean timedOut = false; for (;;) { int c = ctl.get(); // 获取线程池运行状态 int rs = runStateOf(c); /* * 此条件可等价地转换为以下 2 个条件, * 只要有一个条件满足,则将线程池中线程数目 - 1,并返回 null * 1、rs >= SHUTDOWN && rs >= STOP * 线程池状态是 STOP (1)、TIDYING (2)、TERMINATED (3) 的其中一种 * 2、rs >= SHUTDOWN && workQueue.isEmpty() * 线程池状态不是 RUNNING (-1) 且工作队列为空 */ if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 将线程池中线程数目-1,并返回null decrementWorkerCount(); return null; } // 获取线程池中线程数目 int wc = workerCountOf(c); /* * timed 用于标记是否需要进行超时控制 * 1、allowCoreThreadTimeOut * 是否允许核心线程超时,默认为 false * 2、wc > corePoolSize * 当线程池中线程数目大于核心线程数时, * 需要对非核心线程进行超时控制 */ boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; /* * 如果线程池中线程数目(wc) > 最大线程数(maximumPoolSize)或 * timed && timedOut(即当前操作需要进行超时控制且上次从工作队列中获取任务失败), * 满足上述条件之一后,再进行判断后续条件 * 如果线程池中线程数目(wc) > 1或工作队列(workQueue)为空,则满足条件 */ /* * 此条件可等价地转换为以下 4 个条件, * 只要有一个条件满足,则将线程池中线程数目 - 1, * 并返回 null,如果失败,则返回重试 * 1、wc > maximumPoolSize && wc > 1 * 线程池中线程数目大于最大线程数且大于 1 * 2、wc > maximumPoolSize && workQueue.isEmpty() * 线程池中线程数目大于最大线程数且工作队列为空 * 3、(timed && timedOut) && wc > 1 * 即当前操作需要进行超时控制且上次从 * 工作队列中获取任务超时且线程池中线程数目大于 1 * 4、(timed && timedOut) && workQueue.isEmpty() * 即当前操作需要进行超时控制且上次从 * 工作队列中获取任务超时且工作队列为空 */ if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { /* * 如果 timed 为 true,则表示需要对 workQueue 的 poll() 方法 * 进行超时控制,如果在 keepAliveTime 时间内没有获取到任务,则返回 null; * 如果 timed 为 false,则表示通过 workQueue 的 take() 方法来获取任务, * 若此时工作队列为空,则 take() 方法会进行阻塞,直到工作队列不为空,可获得任务为止 */ Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
- getTask() 方法主要用于从工作队列中获取任务
-
execute(Runnable command)
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); // 获取 ctl int c = ctl.get(); /* * 如果线程池中线程数目小于 corePoolSize, * 则在线程池中创建一个新的线程来执行任务 */ if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; // 重新获取 ctl c = ctl.get(); } // 如果当前线程池状态为 RUNNING,且往队列中添加任务成功 if (isRunning(c) && workQueue.offer(command)) { // 重新获取 ctl int recheck = ctl.get(); /* * 再次判断线程池的状态是否为 RUNNING, * 如果不是,则移除之前添加进队列的任务 */ if (! isRunning(recheck) && remove(command)) // 使用线程拒绝策略对任务进行处理 reject(command); // 获取线程池中线程数量,并判断其是否等于 0 else if (workerCountOf(recheck) == 0) // 如果为 0,则在线程池中创建一个新的线程 addWorker(null, false); } /* * 在线程池中创建一个新的线程来执行任务 * 如果创建失败,则使用线程拒绝策略对任务进行处理 */ else if (!addWorker(command, false)) reject(command); }
-
execute() 方法主要用于提交任务,交由工作线程来进行处理
-
任务执行流程
-
当线程池接收到新任务时,先判断当前线程数量是否小于核心线程数 (corePoolSize),如果小于,则即使其他线程都处于空闲状态,也会创建新的线程 (核心线程) 来处理新任务
-
如果当前线程数量大于或等于核心线程数 (corePoolSize),则再判断工作队列 (workQueue) 是否有空位,如果有,则将新任务添加到工作队列 (workQueue) 中,等待空闲线程去获取处理
-
如果工作队列 (workQueue) 没有空位,则再判断当前线程数量是否小于最大线程数 (maximumPoolSize),如果小于,则会创建新的线程 (非核心线程) 来处理任务
-
如果当前线程数量大于或等于最大线程数 (maximumPoolSize),则使用线程拒绝策略来处理新任务
-
-