了解原理,从使用开始,步步深挖。线程池提交任务分为两种 execute() 和 submit()。让我们一起窥探这并不神秘的原理吧!
- 还不知道线程池怎么使用的,可以先看 java 线程池的使用及原理(一):线程池的使用
1. execute()
- 由之前的文章可知, new ThreadPoolExecutor() 只是初始化了一些属性,真正的运行,是从 executor() 开始的。
1.1 开始窥探 execute() 的执行原理
1.1.1 execute() 源码如下:
- execute 主要分为三部分:
- 第一部分:判断 线程池的线程数workerCountOf© < 核心线程数 corePoolSize,直接添加 Worker
- 第二部分:第一部分不成立的情况下,Running状态添加一个任务进 工作队列workQueue ,成功后再次检查线程池状态
- 第三部分:第一部分不成立的情况下,工作队列添加任务失败(队列已满),则尝试添加 Worker 即新线程
public void execute(Runnable command) {
/* 若空任务,则报空指针异常 */
if (command == null)
throw new NullPointerException();
/* 获取ctl 的值,高位前三位存储 线程池状态,后面29位存储线程个数 */
int c = ctl.get();
/* 第一部分:
* workerCountOf(c) <==> return c & CAPACITY; 获取正在执行的线程个数,然后与 corePoolSize 比较是否大于核心线程数
*/
if (workerCountOf(c) < corePoolSize) {
/* 没超过corePoolSize,直接添加任务 */
if (addWorker(command, true))
return;
c = ctl.get();
}
/* 第二部分:
* 超出 corePoolSize 或者 添加任务 addWorker 失败:
* isRunning(c) ==> 检查是否运行状态
* workQueue.offer(command) ==> 往队列添加一个任务
*/
if (isRunning(c) && workQueue.offer(command)) {
/*
* 重新检查线程池状态:
* ! isRunning(recheck) 非Running && remove(command))移除队列任务
* workerCountOf(recheck) == 0 ==> 线程个数为 0,添加一个 null 任务
*/
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/* 第三部分:线程队列也已经满了,尝试添加非核心的线程 */
else if (!addWorker(command, false)) // 添加任务失败
reject(command);
}
1.1.2 再来细看 addWorker 如何工作
- 方法用到比较少见的标记式退出多层循环,可 跳转学习 再继续往下看
- Worker 就是一个内置Thread对象的Runnable,后面会有具体的解释
- addWorker 分为两大部分:
- 第一部分:外层 for 死循环 嵌套 内层 for 死循环
- 外层 for 循环:主要用于判断线程池的状态,是否有必要继续往下执行
- 内层 for 循环:主要用于更改 workerCountOf 工作线程数
- 第二部分:创建一个Worker,并使用内置对象Thread执行任务
- 第一部分:外层 for 死循环 嵌套 内层 for 死循环
/* param1: 线程任务 param2: 是否核心线程 */
private boolean addWorker(Runnable firstTask, boolean core) {
/* 第一部分 外层 for 死循环 嵌套 内层 for 死循环 */
retry:
/* 外层 for 循环 */
for (;;) {
/* 获取ctl值与线程池状态rs */
int c = ctl.get();
int rs = runStateOf(c);
/* 这里逻辑有点绕
* 1. rs >= SHUTDOWN 即非Running,Running 直接退出判断
* 2. rs == SHUTDOWN 不解释,firstTask == null 任务为空,! workQueue.isEmpty() 队列不为空
* 3. !(2) 即 SHUTDOWN 状态下,队列不为空还需要放行,其余STOP、TIDYING、TERMINATED 状态直接返回 false
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
/* 内层 for 循环 */
for (;;) {
/* 获取工作的线程池数wc:
* wc >= CAPACITY 是否大于等于能够存储的最大线程数
* wc >= (core ? corePoolSize : maximumPoolSize)) 如果创建的是核心线程,是的话与corePoolSize比较,不是的话与maximumPoolSize比较
*/
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
/* CAS 设置 ctl+1,成功直接退出retry循环 */
if (compareAndIncrementWorkerCount(c))
break retry;
/* CAS 失败重新获取ctl,
* 如果 runStateOf(c) != rs 线程池状态发生变化,continue retry 循环即退出到retry 循环继续执行
* 否则在内层循环,直至return 或 CAS 更改成功
*/
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;
Worker w = null;
try {
/* 创建 Worker 对象,并获取 Thread t */
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
/* 使用内置的 ReentrantLock 确保代码同步 */
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();
/* 维护了一个 HashSet<Worker> workers,往set 添加 worker */
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
/* start(): 线程开启的地方 */
t.start();
workerStarted = true;
}
}
} finally {
// 线程池状态发生改变才会执行
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
/* 移除任务,工作线程-1,尝试终止线程池 */
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
1.1.3 细谈 Worker 类
- Worker 实现了 Runnable,又在构造方法 getThreadFactory().newThread(this); 中将 Worker 作为参数创建新线程,所以 thread.start() 会执行 Worker 重写的 run() 方法。
/* 继承AQS,即Worker 也是抽象队列同步器 */
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
/* 不会用到,只是为了屏蔽javac的 warning */
private static final long serialVersionUID = 6138294804551838833L;
final Thread thread;
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/* 构造方法赋值了一些属性,并从线程工厂创建了一个新线程 */
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);
}
/* 是否独占 */
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) {
}
}
}
}
1.1.4 runWorker(this); 真正执行传入任务的地方
final void runWorker(Worker w) {
Threadwt = 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();
/* 根据线程池判断线程是否应该被中断
* 1. runStateAtLeast(ctl.get(), STOP) <==> return c >= s 即是否非Running && Shutdown,此状态下线程应该被中断
* 2. Thread.interrupted() 返回线程中断状态,false 表示没被中断过,立即跳转3,true 表示已经被中断过,继续判断线程池状态
* 3. 线程池肯定是关闭状态,需要中断当前线程,false 表示没被中断,即应该执行 wt.interrupt();
*/
if (
/* 1. */
(runStateAtLeast(ctl.get(), STOP) ||
/* 2. */
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
/* 3. */
&& !wt.isInterrupted())
wt.interrupt();
try {
/* 空方法,可以看作是预留方法 */
beforeExecute(wt, task);
Throwable thrown = null;
try {
/* 这里的task,就是传入进来的 runnable */
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);
}
}
1.1.5 先来一波小总结
-
进入 executor(),先进行一系列的判断,得出应该添加哪一种 Worker,即执行哪种 addWorker()
-
addWorker() ,第一部分先判断是否应该添加新线程执行任务,第二部分 w = new Worker(),w.thread.start() 即执行 worker 的 run 方法
-
runWorker 先执行 线程池状态判断,然后执行 w.firstTask.run(),即传入 runnable 的 run 方法,执行完毕,万事大吉。
-
愉快之余,思考接下来的问题,什么时候执行等待队列workqueue的任务,如何保持核心线程不被销毁。别急,一个一个来。
1.2 workqueue 队列中的如何被执行
- 从 execute() 方法可知,当 workerCountOf© >= corePoolSize && isRunning© 加入 workQueue,那么,应该是什么方法,从 workQueue 取出任务来
- 回想整个执行过程,只有 runWorker 中的 getTask() ,貌似与之相关,查看源码,一看还真与 workQueue 相关
private Runnable getTask() {
// poll获取任务是否超时
boolean timedOut = false;
for (;;) {
/* 老规矩,获取 ctl 和 线程池状态 */
int c = ctl.get();
int rs = runStateOf(c);
// STOP状态 以上不处理任务 && ShUTDOWN 只处理已添加的任务,即线程池处于关闭状态
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
/* 是否有超时机制timed
1. allowCoreThreadTimeOut 允许核心线程空闲超时后回收
2. wc > corePoolSize 代表非核心线程空闲均会超时回收
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/* 有四种判断条件的组合
* 1. wc > maximumPoolSize && wc > 1
* 2. wc > maximumPoolSize && workQueue.isEmpty()即
* 3. (timed && timedOut) && wc > 1
* 4. (timed && timedOut) && workQueue.isEmpty()
*/
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
/* 根据 timed 的判断,确定获取 workQueue 的任务的方式,后面有详解 */
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
/* 没能正确获取r,即获取超时 */
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 小总结:for死循环 一直获取task任务,配合 runWorker 的 while 循环,确保队列的任务会被执行。
1.2.1 LinkedBlockingQueue 类
- 看一个类,先看构造方法,在看静态属性
- 构造函数指定队列的允许长度,并创建了一个空节点指向 head、last
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
- 静态属性
/* 指定版本号 */
private static final long serialVersionUID = -6903933977591709194L;
/* 队列界限 与 Integer原子类 */
private final int capacity;
private final AtomicInteger count = new AtomicInteger();
/* 头节点与尾节点 */
transient Node<E> head;
private transient Node<E> last;
/* 锁 与 Condition */
/* take, poll 等出队锁 */
private final ReentrantLock takeLock = new ReentrantLock();
/* 等待出队的条件唤醒队列 */
private final Condition notEmpty = takeLock.newCondition();
/* put, offer 等进队锁*/
private final ReentrantLock putLock = new ReentrantLock();
/** 等待进队的条件唤醒队列 */
private final Condition notFull = putLock.newCondition();
1.2.2 队列任务进队
- workQueue.offer()
public boolean offer(E e) {
if (e ==null) throw new NullPointerException();
final AtomicInteger count = this.count;
/* 队列已满,返回 false */
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
/* 入队锁 */
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
/* 入队操作,count++ */
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
/** 实际执行的是 notEmpty.signal(); */
if (c == 0)
signalNotEmpty();
return c >= 0;
}
1.2.3 队列任务取出的两种方式
- 1、workQueue.poll(),超出等待时间返回 false
/* param1:0L param2:时间单位 */
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
/* 获取出队锁 */
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
return null;
/* 出队notEmpty 等待唤醒 */
nanos = notEmpty.awaitNanos(nanos);
}
/* 取出第一个节点,head 不存储 item */
x = dequeue();
/* count-- */
c = count.getAndDecrement();
/* 如果 count > 1,即队列不只有一个node,唤醒其他线程来帮忙 */
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
/* 实际执行的是 notFull.signal(); */
if (c == capacity)
signalNotFull();
return x;
}
- 2、workQueue.take() ,与 poll() 基本类似,就是少了while 循环 的时间参数
public E take() throws InterruptedException {
Ex;
int c = -1;
final AtomicInteger count = this.count;
/* 出队锁 */
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
1.3 线程池如何使核心线程一直执行
- 试想,我们在 new Thread() 的时候,执行完 start() 之后,没进行处理线程自动关闭。那是不是,线程池的线程在执行完任务之后,进行了特殊处理呢?
- 回看 getTask() 的源码,
/* 1. allowCoreThreadTimeOut 是否允许核心线程过时,默认false
* 2. wc > corePoolSize; 即只有核心线程,一直执行 workQueue.take();
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
- 查看 workQueue.take(); 源码
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
/* 核心在这: count == 0,await() 保持睡眠 */
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
- 问题又来了,核心线程阻塞,又是什么时候被唤醒的?
- 当 workerCountOf© == corePoolSize ,会执行 workQueue.offer(),可以看到,会执行signal() 方法
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
- 能不能设置核心线程超时? 当然可以
- 注意,设置allowCoreThreadTimeOut(true) ,keepAliveTime 不能为 0,否则报错 IllegalArgumentException: Core threads must have nonzero keep alive times
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class AllowCoreThreadTimeOutTest {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 10,
1L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(2));
System.out.println("========== 我来打印点东西 =========");
threadPool.allowCoreThreadTimeOut(true);
}
}
-
可以看到,线程池没有执行shutdown(),依旧被关闭了。
-
keepAliveTime 为 0 报错信息
1.4 ThreadFactory 线程工厂
- Excutors 内置了 DefaultThreadFactory implements ThreadFactory,通过 DefaultThreadFactory 去 new Thread
- 很简单的一个内部类,看不懂的可以移步至 java 线程的创建方式与线程的六种状态切换
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
2. submit()
- submit() 有三个重载函数
- 函数1:Future<?> submit(Runnable task):null 返回值
- 函数2: Future submit(Runnable task, T result):T 类型返回值
- 函数3: Future submit(Callable task):Callable 返回值
- 源码中可以看到,都是先调用 newTaskFor ,返回值再调用 execute()
/* 函数1 */
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
/* 函数2 */
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;
}
/* 函数3 */
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
- 再来看 newTaskFor 源码,有两个重载函数,对应 Runnable、Callable
/* 对应 submit() 函数1、2 */
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
/* 对应 submit() 函数3 */
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
- 总结:submit() 先调用了 newTaskFor() 生成 FutureTask 对象(本质是 Runnable),将 FutureTask 对象当做参数执行 execute(Runnable runnable)。
3. 小总结
- execute() 中的多线程 是使用 实现 Runnable 方式创建的线程;
- sumbit() 中的多线程 使用的是 Callable & Future 的方式创建的线程。