ThreadPoolExecutor前提与介绍
- ThreadPoolExecutor是JAVA线程池的核心实现类之一。
前置知识
- 了解Runable\Callable
- 了解Thread
- 掌握AQS阻塞队列
总体概括
- java线程池可以对线程进行复用,减少创建和销毁线程的次数,减少资源浪费。
- java线程池有核心线程数、最大线程数、阻塞队列、饱和策略四个概念。
- 若线程池线程数小于核心线程数,则新任务都会交由一个新的线程执行,无论已有的线程是否空闲。
- 若线程池线程数大于等于核心线程,小于最大线程数时,新任务到来会先判断是否有空闲的线程,如果有则先让空闲线程执行,没有则创建新的线程执行。
- 若线程池线程数等于最大线程数,则新到来的任务会加入阻塞队列。再加入阻塞队列时,若阻塞队列对队列长度有限制,则可能导致加入失败,若失败则根据饱和策略拒绝当前的任务。
- 饱和策略分为四种:
- 直接丢弃当前任务。
- 丢弃当前任务并抛异常。
- 丢弃之前的任意一个任务并把当前任务排到最前面。
- 用当前线程执行该任务。
ThreadPoolExecutor核心属性
状态属性及其对应的方法
/**
* 用于计数线程池中线程的数量和存储当前线程池的状态
* 高4位用于存储线程池状态
* 低28位用于存储当前线程池线程数。
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
/**
* 最大容量掩码
* 把掩码与ctl做与操作即可获取到当前线程数。
*/
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 运行中,初始化后就是该状态
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;
/**
* CAPACITY是低28都为1的掩码,
* c & CAPACITY 可以获取到低位的值,该值表示线程池线程数量
*
* @param c 一般是ctl的局部变量
* @return 获取到当前线程池线程的数量
*/
private static int workerCountOf(int c) { return c & CAPACITY; }
/**
* ~CAPACITY是高4位为1的掩码
* c & ~CAPACITY 可以获取到高位的值,该值表示线程池的状态
*
* @param c 一般是ctl的局部变量
* @return 获取到当前线程池的状态。
*/
private static int runStateOf(int c) { return c & ~CAPACITY; }
/**
* 根据线程池状态和线程数量包装出对应的ctl
*/
private static int ctlOf(int rs, int wc) { return rs | wc; }
/*
下面三个方法都是比较线程池的状态
根据线程池状态值的定义可知:
RUNNING > SHUTDOWN > STOP > TERMINATED
而且这个顺序是线程池逐渐关闭的状态递增。
@param 下面三个方法的参数c一般都是ctl的局部属性
@param 下面三个方法的参数s一般都是指定的状态属性。
*/
// 状态不小于指定状态
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
// 状态大于等于指定状态
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
// 线程池正在运行中
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
/*
下面两个方法是通过CAS操作当前线程的数量,并确保原子性。
这里只是对记录线程数的ctl属性的操作,不是直接操作线程的集合。
*/
// 线程数+1
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
// 线程数-1
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
复制代码
参数属性
/**
* 阻塞队列
* 用于存储没有分配到线程执行的任务
*/
private final BlockingQueue<Runnable> workQueue;
/**
* Worker是对线程的封装类
* 这里用于存储线程池的所有线程。
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
* 锁
*/
private final ReentrantLock mainLock = new ReentrantLock();
/**
* 锁状态
*/
private final Condition termination = mainLock.newCondition();
/**
* 记录最多线程数
*/
private int largestPoolSize;
/**
* 完成的任务数
*/
private long completedTaskCount;
/**
* 线程工厂
* 用于线程池创建新线程
*/
private volatile ThreadFactory threadFactory;
/**
* 拒绝策略执行
* 用于新任务被拒绝执行时调用
* 默认拒绝策略是AbortPolicy,即抛出异常。
*/
private volatile RejectedExecutionHandler handler;
/**
* 保留时间
* 当线程数超过核心线程数是,空闲线程在等待一定时间后都没有获取到新任务则会被释放
* 该字段就是描述空闲线程等待的时间。
*/
private volatile long keepAliveTime;
/**
* 核心线程是否释放
* 若为true,核心线程等待keepAliveTime后则被释放。
* 若为false,则核心线程空闲也不会被释放
*/
private volatile boolean allowCoreThreadTimeOut;
/**
* 核心线程数
*/
private volatile int corePoolSize;
/**
* 最大线程数
*/
private volatile int maximumPoolSize;
复制代码
ThreadPoolExecutor内部类讲解
- Worker: 工作线程,任务执行的基本单位,可以复用线程。
- 拒绝策略:ThreadPoolExecutor的拒绝策略接口及4个自身实现的拒绝策略。
Worker
/**
* Worker是ThreadPoolExecutor最基本的执行单位。
* Worker实现了Runable接口,可以作为独立线程运行。
*
* Worker继承了AQS的锁阻塞机制,实现了获取锁的方法
* worker在执行任务的run方法时,会锁定worker本身,
* 而在从阻塞队列获取任务的过程中则没有上锁。
* 通常可以通过尝试获取worker的锁来判断worker是否空闲
* 在SHUTDOWN下,worker通常在空闲情况下才会被打断。
* 若在STOP状态,则worker即使在执行任务也会被打断。
*/
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
/**
* 当前worker管理的线程
*/
final Thread thread;
/**
* 用于初始化worker时执行的第一个任务
*/
Runnable firstTask;
/**
* 用于计算当前worker执行的任务数
*/
volatile long completedTasks;
/**
* 构造器
*/
Worker(Runnable firstTask) {
setState(-1); // 只要state不是0即上锁
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/**
* 启动方法
*/
public void run() {
// 调用了ThreadPoolExecutor的方法
// 这里只是作为一个代理。
// 方法在下面展示
runWorker(this);
}
// 实现了AQS获取锁的方法
protected boolean tryAcquire(int unused) {
// 使用CAS把锁状态更换成1
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 实现了AQS释放锁的方法
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0); // state为0则为没上锁
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
// 如果当前worker已在运行中,则中断它。
void interruptIfStarted() {
Thread t;
// 当前工作线程已上锁(正在执行任务逻辑)
// 且线程不为空
// 且没有被中断
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt(); // 强行中断
} catch (SecurityException ignore) {
}
}
}
}
复制代码
拒绝策略
拒绝策略用于任务不被线程池接收时做出的对应处理。
- RejectedExecutionHandler 拒绝策略接口定义
- AbortPolicy 直接抛出异常
- CallerRunsPolicy 当前执行该策略的线程执行该任务
- DiscardPolicy 直接丢弃任务,不做任何处理
- DiscardOldestPolicy 抛弃最近即将执行的任务,并重新尝试把当前任务加入任务列表。
RejectedExecutionHandler接口
/**
* 该接口用于定义无法被线程池接收时对任务的处理策略。
* 当线程数超过了最大线程数,或者线程池已关闭却接收到新任务时被触发。
*/
public interface RejectedExecutionHandler {
/**
* 任务被拒绝的回调方法。
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
复制代码
AbortPolicy
/**
* ThreadPoolExecutor提供的拒绝策略之一,默认的拒绝策略
* 直接抛出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());
}
}
复制代码
CallerRunsPolicy
/**
* ThreadPoolExecutor提供的拒绝策略之一
* 直接使用当前线程执行被拒绝任务
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
复制代码
DiscardPolicy
/**
* ThreadPoolExecutor提供的拒绝策略之一
* 直接丢弃,不做任何处理
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
复制代码
DiscardOldestPolicy
/**
* ThreadPoolExecutor提供的拒绝策略之一
* 从阻塞队列中取出一个即将执行的任务丢弃,重新尝试执行该任务。
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
复制代码
ThreadPoolExecutor核心逻辑追踪
执行任务逻辑
- execute方法:执行无返回值任务
- addWorker方法:添加新的工作线程
- reject方法:拒绝任务策略
- submit方法:执行任务,且有返回值。
execute方法
/**
* execute是线程池执行线程的方法。
*/
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();
}
// 运行到这表示线程池线程数大于等于核心线程数
// 判断线程池是否在运行中,且是否成功把任务添加到队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 如果线程池没在运行则拒绝该任务
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果线程池仍在执行,而线程数为0
else if (workerCountOf(recheck) == 0)
addWorker(null, false); // 尝试新建一个线程
}
// 尝试添加一个新线程
else if (!addWorker(command, false))
reject(command); // 执行拒绝策略。
}
复制代码
addWorker方法
/**
* 尝试添加新的线程
* execute通过调用该方法添加新的线程
* @return 若添加成功则返回true,否则返回false
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 检查线程池是否已经关闭
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 通过CAS增加线程数
for (;;) {
int wc = workerCountOf(c); // 获取线程数
// 判断是否超过线程限制
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 通过CAS添加线程数,成功则跳出循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
// 判断ctl是否被更改
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
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());
// 判断线程池是否正在运行
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 这里表示线程池还未激活线程,线程却已经被激活
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s; // 记录最大线程数
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 启动线程,执行worker的run方法
workerStarted = true;
}
}
} finally {
if (! workerStarted)
// 如果线程加入失败,则调用加入失败方法。
addWorkerFailed(w);
}
return workerStarted;
}
复制代码
reject方法
/**
* 执行拒绝策略
* execute在尝试把添加任务失败时,会执行拒绝策略方法。
*
*/
final void reject(Runnable command) {
// private volatile RejectedExecutionHandler handler;
// 默认是AbortPolicy,即直接抛异常。
handler.rejectedExecution(command, this);
}
复制代码
submit方法
/*
* submit方法是执行有返回值的任务。
* 下面三个方法都是在AbstractExecutorService中实现。
* ThreadPoolExecutor是AbstractExecutorService实现类,
* 且继续使用了AbstractExecutorService的逻辑。
*
* 三个方法都是把传入的参数封装成RunnableFuture,
* 再调用execute方法执行。
*/
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
复制代码
复用线程逻辑
前面回顾
- 开发者调用了ThreadPoolExecutor的execute方法,传入Runnable执行的内容。
- ThreadPoolExecutor调用了addWorker方法尝试添加任务进线程池。
- addWorker方法在创建了worker执行任务后,在最后会通过调用worker的start方法激活worker。
- worker的run方法只调用了ThreadPoolExecutor的runWorker方法。
runWorker方法
/**
* worker的执行逻辑
* 该方法不断从阻塞队列中获取任务执行。
* 若获取不到任务,则根据配置判断是否要释放线程。
*/
final void runWorker(Worker w) {
// 获取worker当前的线程
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// ThreadPoolExecutor中若需要中断当前worker线程
// 需要提前获取worker的锁。
// 此处worker解锁可以确保该线程能够被中断。
// worker重写了释放锁方法,即使没有锁状态释放锁也不会报错,
// 且可以把锁置为解锁的状态。
w.unlock();
boolean completedAbruptly = true;
try {
// 每次循环通过getTask方法获取新的任务。
// 当获取的任务为空时,当前worker则会结束并被销毁。
while (task != null || (task = getTask()) != null) {
w.lock(); // 上锁
// 如果线程池是STOP、TIDYING、TERMINATED状态
// 而线程没有被中断,则中断当前线程
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 这是一个保留方法
// 可以在任务开始执行之前做初始化操作
// ThreadPoolExecutor在这里是空方法
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run(); // 调用任务的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 {
// 这是也是一个保留方法,跟beforeExecute相似
// 可以在任务开始执行之后做初始化操作
// ThreadPoolExecutor在这里也是空方法
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++; // 统计执行过的任务数
w.unlock(); // 解锁
}
}
completedAbruptly = false;
} finally {
// worker被销毁的清理方法。
// 这里包括把当前worker从线程集合移除,线程数减一等操作。
processWorkerExit(w, completedAbruptly);
}
}
复制代码
getTask方法
/**
* 从阻塞队列中获取任务
* 若返回null,则worker会被释放销毁
*/
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 判断线程池是否关闭
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 若关闭则清空所有线程
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c); // 获取当前线程数
// 当前线程是否会超时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 当前线程数是否超出了最大线程数量
// 或者当前线程已超时
// 满足上面两个条件之一,且线程数大于1或者阻塞队列为空,
// 则尝试减少一个worker计数并返回任务为空。
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;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
复制代码
processWorkerExit方法
/**
* 当一个worker被销毁时执行
*
* worker被销毁有两种情况,
* 一种是getTask时获取到的任务是null
* 另一种是run的时候出现异常被捕捉,worker也会被销毁
*
* @param w 表示要被销毁的worker
* @param completedAbruptly 表示是否因为异常而销毁
*/
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly)
// 若因为异常而执行该方法
// ctl中的线程计数则还没有被调整
// 这里用CAS+自旋调整ctl值。
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); // 上锁
try {
// 把当前worker的任务计数累加到线程池中。
completedTaskCount += w.completedTasks;
workers.remove(w); // 线程集合移除该worker
} finally {
mainLock.unlock();
}
// 当线程数减少时都会调用该方法
// 判断线程池是否要关闭,若是则帮忙中断空闲线程。
tryTerminate();
int c = ctl.get();
// 如果线程池正在运行
if (runStateLessThan(c, STOP)) {
// 如果是获取不到新任务而被销毁
if (!completedAbruptly) {
// 这里计算需要的worker的最小值
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 如果当前工作线程数大于最小线程数,则返回
if (workerCountOf(c) >= min)
return;
}
// 如果当前工作线程数小于最小线程数,则创建一个新工作线程。
addWorker(null, false);
}
}
复制代码
关闭线程池逻辑
- shutdown方法:关闭线程池
- tryTerminate方法:尝试终止线程池
- interruptIdleWorkers方法:遍历并终止空闲线程
- shutdownNow方法:立刻关闭线程池
- interruptWorkers方法:终止活跃的工作线程
shutdown方法
/**
* ThreadPoolExecutor外部通过调用该方法关闭线程池
* 该方法被执行后,线程池状态被设为SHUTDOWN,
* 该状态下线程池不再接收新的任务,但会继续执行队列中的任务,
* 直到全部执行完毕。
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess(); // 确定有关闭线程的权限
advanceRunState(SHUTDOWN); // 设为关闭状态
interruptIdleWorkers(); // 中断所有空闲工作线程
onShutdown(); // 空方法,用于被重写。
} finally {
mainLock.unlock();
}
// 尝试终止线程池
tryTerminate();
}
复制代码
tryTerminate方法
/**
* 该方法尝试帮忙从SHUTDOWN状态过渡到TERMINATED状态。
*/
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 如果线程池还在运行状态
// 或者已经处于整理状态或者终止状态
// 或者线程池处于关闭状态但任务队列还未为空
// 则直接返回,不做处理操作
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 若不符合上面情况,且工作线程数不为0
// 则尝试中止一个工作线程
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
return;
}
// 来到这里表示当前处于SHUTDOWN或STOP态,
// 且工作线程为0
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 用CAS把线程池状态设为整理态。
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 用于终止线程池时执行的额外操作。
// 这里是一个空方法
terminated();
} finally {
// 把线程池设为终止态。
ctl.set(ctlOf(TERMINATED, 0));
// 唤醒其他等待mainLock的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
复制代码
interruptIdleWorkers方法
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
/**
* 终止空闲的工作线程
*
* @param onlyOne 是否只中断一个工作线程
*/
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 遍历线程
for (Worker w : workers) {
Thread t = w.thread;
// 判断t是否被中断且处于空闲
// 若能获取到锁表示w当前空闲。
if (!t.isInterrupted() && w.tryLock()) {
try {
// 若空闲则中断它
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
// 若标志了onlyOne,
// 则无论第一个工作线程是否被中断
// 都返回。
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
复制代码
shutdownNow方法
/**
* 立刻关闭线程池
* 把线程池状态设为STOP
* 所有工作线程无论是否活跃都会被中断。
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP); // 设为STOP状态
interruptWorkers(); // 中断活跃状态下工作线程
tasks = drainQueue(); // 获取剩余未执行的任务
} finally {
mainLock.unlock();
}
// 终止线程池
// STOP状态下会尝试终止空闲线程
// 若所有工作线程都为空,则把线程池转化为终止态。
tryTerminate();
return tasks; // 返回没有执行的任务列表
}
复制代码
interruptWorkers方法
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
// 调用worker自身实现的中断方法
// 这里只中断了活跃状态下的工作线程
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
复制代码
Executors工厂
Executors 是java为ThreadPoolExecutor提供的工厂类。
-
固定线程池:最大线程数和核心线程数一致。
-
缓存线程池:核心线程数为0,最大线程数是Integer的最大值
固定线程池
/**
* 指定线程数,最大线程与核心线程数一致。
* 若核心线程不被保留,则一旦获取不到任务就会被释放(默认是保留)
* 使用LinkedBlockingQueue作为队列,该队列长度为int的最大值。
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* 该方法与上面类似,唯一区别是指定了线程工厂。
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
复制代码
缓存线程池
/**
* 核心线程数为0,最大线程数是无限大
* 线程保留60秒,或60秒内没有新任务才销毁
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* 该方法与上面类似,唯一区别是指定了线程工厂。
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
复制代码
总结
线程池状态总结
- 一开始创建及其运行时都处于运行态RUNNING。
- 运行态可以转变为关闭态或者停止态。
- 若调用shutdown方法,则进入关闭态。
- 关闭态不再接收任务,但会继续处理剩余的任务。
- 若所有任务都被执行,任务列表为空,则会逐渐把没有获取到的任务的工作线程停止销毁。
- 关闭态下一个状态通常是整理态。
- 若调用了shutDownNow方法,则会进入停止态STOP。
- 该状态下会中断所有线程,从而尽快过渡到下一个装态。
- 停止态下一个状态是整理态。
- 若所有工作线程都停止,则进入到在整理态TIDYING。
- 该状态下会执行termined方法,用于回收清理操作。ThreadPoolExecutor的该方法是空方法,仅保留为子类重写使用。
- 整理态下一个状态是终止态。
- 若整理态执行完对应的清理工作,则进入终止态TERMINED,线程池彻底关闭。
worker的锁及其工作流程
- worker是ThreadPoolExecutor中最基本的线程执行单位,且拥有复用线程的能力。
锁
- Worker因为继承并实现AQS而拥有了锁的方法。
- 为了避免worker在执行任务过程中被中断,在执行任务前后都会加锁解锁。worker被加锁时处于活跃态,没有锁时则处于空闲态。
- worker的锁某种程度上像一个带保护措施标记,该锁的解锁可以在没加锁的情况下正常执行。
工作流程
- 活跃态:执行任务的run方法。此时持有锁,且一旦有异常被抛出,则终止该线程,执行该worker销毁操作。若顺利执行完毕,则解锁进入空闲态。
- 空闲态:获取任务。该状态下没有获取锁,可能会被中断。空闲状态主要是去获取任务,获取时会判断线程池状态,当前线程状态(是否被中断),是否有未完成任务,是否要释放当前worker等。若能获取到的任务为空,则表示获取任务失败,当前的worker会被销毁。
线程池机制
核心线程默认会一直保留,最大线程在一定时间内没有领取到任务会被销毁,超出最大线程的任务会被阻塞队列缓存,若阻塞队列放不下则会采取拒绝策略。