前言:
上一篇单线程池的随笔提到了非固定线程数
的线程池,那么本文就以前文为基础,剖析Java线程池中的Executors.newCachedThreadPool()
。Executors.newCachedThreadPool()
,后文简称为缓存线程池
。
Executors.newCachedThreadPool()源码解析
先看构建缓存线程池的
源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
缓存线程池
执行的时候,参数可以以此解释为:核心线程数0,最大线程数为Integer最大值,存活时间为60秒,然后以SynchronousQueue
作为队列使用。
ExecutorService.execute的执行过程
在上一文中,ExecutorService.execute(command)
的主要代码为:
int c = ctl.get();
//1.
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.
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);
}
//3.
else if (!addWorker(command, false))
reject(command);
根据debug断点,描述一个execute的执行过程:
- 提交第一个任务,第一步,肯定是返回false;
- 第二步,
workQueue.offer(command)
直接返回false
; - 第三步,然后执行
!addWorker(command, false)
,关于此逻辑不再赘述,上一文中已分析,而对于缓存线程池
中addWorker
最核心的的是,Worker
执行第一个Runnable
任务,待执行完毕,然后执行while
循环,去getTask()
; - 执行到
getTask()
中的workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
逻辑,会根据keepAliveTime
阻塞; - 最关键的来了,这个时候,提交第二个
Runnable
任务,在workQueue.offer(command)
的时候返回了true
,与此同时,第一个任务的Worker
的workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
获取到了第二个任务的command
;
上面的execute执行过程画个简易的流程图
此外,还进行了补充:如果workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
超时,那么此Node
节点会被清除,Worker
工作线程也会被杀掉。
那么,这个SynchronousQueue
阻塞队列究竟是怎么工作的呢,下面一节做详细介绍。
核心组件SynchronousQueue解析
进入new SynchronousQueue
对象的源码可以看见:
public SynchronousQueue() {
this(false);
}
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
默认的SynchronousQueue
对象,fair
是false
,见名知意非公平
,这一下子让我联想到可重入锁ReentrantLock
的非公平锁,随后,根据fair
为false
最终使用TransferStack
作为transferer
的默认对象。
SynchronousQueue默认数据结构TransferStack栈
看到栈这种数据结构,它的特点就是先进后出。那么我们先看构造方法的源码:
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E e = transferer.transfer(null, true, unit.toNanos(timeout));
if (e != null || !Thread.interrupted())
return e;
throw new InterruptedException();
}
可以发现,offer
和poll
方法最终都是执行的transferer.transfer
方法。只不过参数上有差异,offer
的e
对象是有值的,而poll
的e
是空的。
那么,TransferStack
的transfer()
方法里在做什么呢,下面看源码:
E transfer(E e, boolean timed, long nanos) {
SNode s = null; // constructed/reused as needed
int mode = (e == null) ? REQUEST : DATA;
for (;;) {
SNode h = head;
if (h == null || h.mode == mode) { // empty or same-mode
if (timed && nanos <= 0) { // can't wait
if (h != null && h.isCancelled())
casHead(h, h.next); // pop cancelled node
else
return null; // [注释1]
} else if (casHead(h, s = snode(s, e, h, mode))) {
SNode m = awaitFulfill(s, timed, nanos); // [注释2]
if (m == s) { // wait was cancelled
clean(s);
return null;
}
if ((h = head) != null && h.next == s)
casHead(h, s.next); // help s's fulfiller
return (E) ((mode == REQUEST) ? m.item : s.item);
}
} else if (!isFulfilling(h.mode)) { // try to fulfill // [注释3]
if (h.isCancelled()) // already cancelled
casHead(h, h.next); // pop and retry
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
for (;;) { // loop until matched or waiters disappear
SNode m = s.next; // m is s's match
if (m == null) { // all waiters are gone
casHead(s, null); // pop fulfill node
s = null; // use new node next time
break; // restart main loop
}
SNode mn = m.next;
if (m.tryMatch(s)) {
casHead(s, mn); // pop both s and m
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match
s.casNext(m, mn); // help unlink
}
}
} else { // help a fulfiller
SNode m = h.next; // m is h's match
if (m == null) // waiter is gone
casHead(h, null); // pop fulfilling node
else {
SNode mn = m.next;
if (m.tryMatch(h)) // help match
casHead(h, mn); // pop both h and m
else // lost match
h.casNext(m, mn); // help unlink
}
}
}
}
- 提交第一个任务,执行
offer
,走入注释1
的逻辑,返回null
并最终返回false
; - 第一个任务在
Worker
执行完毕,执行getTask()
,执行poll
,最终走入逻辑注释2
中的awaitFulfill
方法,并阻塞住。下面看看awaitFulfill
方法的逻辑:
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
if (w.isInterrupted())
s.tryCancel();
SNode m = s.match;
if (m != null)
return m;
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
s.tryCancel();
continue;
}
}
if (spins > 0)
spins = shouldSpin(s) ? (spins-1) : 0;
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
else if (!timed)
LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
- 可以发现,在循环了一定次数之后(根据
spins
判断),如果还没有match
的节点,那么执行LockSupport.parkNanos(this, nanos)
进行线程的等待,这里涉及并发编程的其它内容,后面再详解; - 此时,线程池提交了第二个任务,然后执行到了
offer
,此时会进入注释3
中的逻辑,执行m.tryMatch(s)
并成功:
boolean tryMatch(SNode s) {
if (match == null &&
UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
LockSupport.unpark(w);
}
return true;
}
return match == s;
}
m.tryMatch(s)
中,给阻塞的head
节点的match
变量成功赋值,并唤醒了waiter
线程,就是之前的等待线程LockSupport.parkNanos(this, nanos)
。
至此,缓存线程池
的逻辑完成了闭环:首次执行任务1,没有可用线程,则新增Worker
线程并启动,Runnable
任务执行完毕,执行poll
方法加入队列并阻塞,阻塞超时则踢出队列并关闭线程。如果此时遇到线程池提交Runnable
任务2,执行offer
方法时,线程任务1的poll
可以感知,任务2便可在任务1的线程中执行。文字加上上面的流程图便更好理解了。
那么,SynchronousQueue
的数据结构除了默认的TransferStack
,还有队列TransferQueue
。
SynchronousQueue数据结构TransferQueue队列
上面已经比较详细介绍栈了,下面先看看队列(FIFO,先进先出)的transfer
方法的源码,然后我进行一个简单的流程介绍,就不做过多的描述了:
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
boolean isData = (e != null);
for (;;) {
QNode t = tail;
QNode h = head;
if (t == null || h == null) // saw uninitialized value
continue; // spin
if (h == t || t.isData == isData) { // empty or same-mode [注释1]
QNode tn = t.next;
if (t != tail) // inconsistent read
continue;
if (tn != null) { // lagging tail
advanceTail(t, tn);
continue;
}
if (timed && nanos <= 0) // can't wait
return null;
if (s == null)
s = new QNode(e, isData);
if (!t.casNext(null, s)) // failed to link in
continue;
advanceTail(t, s); // swing tail and wait
Object x = awaitFulfill(s, e, timed, nanos);
if (x == s) { // wait was cancelled
clean(t, s);
return null;
}
if (!s.isOffList()) { // not already unlinked
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
s.item = s;
s.waiter = null;
}
return (x != null) ? (E)x : e;
} else { // complementary-mode [注释2]
QNode m = h.next; // node to fulfill
if (t != tail || m == null || h != head)
continue; // inconsistent read
Object x = m.item;
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
advanceHead(h, m); // successfully fulfilled
LockSupport.unpark(m.waiter);
return (x != null) ? (E)x : e;
}
}
}
- 首先在执行完第一次任务1之后,
getTask()
并执行poll
,进入注释1
的awaitFulfill
逻辑:
Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
/* Same idea as TransferStack.awaitFulfill */
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
int spins = ((head.next == s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
if (w.isInterrupted())
s.tryCancel(e);
Object x = s.item;
if (x != e)
return x;
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
s.tryCancel(e);
continue;
}
}
if (spins > 0)
--spins;
else if (s.waiter == null)
s.waiter = w;
else if (!timed)
LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
- 等有其它任务2被线程池提交时,则进入
注释2
的逻辑,将任务放入队列的前方,并唤醒任务1的线程,然后任务1的线程执行任务2.
SynchronousQueue与LinkedBlockingQueue对比
LinkedBlockingQueue | SynchronousQueue.TransferStack | SynchronousQueue.TransferQueue | |
---|---|---|---|
数据结构 | 链表,先进先出(任务commad) | 栈,先进后出(Worker线程) | 链表,先进先出(Worker线程) |
执行offer | 将Runnable 放入队列,直接返回true |
如果是首次执行,返回false。如果有其它线程执行了poll 在等待,那么会将Runnable 给poll线程 ,并返回true |
同SynchronousQueue .TransferStack |
执行poll | 从队列拿出Runnable |
将当前线程放入队列中,等待其它任务提交offer |
同SynchronousQueue .TransferStack |
关于数据结构的解释:
- 上面的
LinkedBlockingQueue
的先进后出的什么意思是:任务来了之后,会将任务放入队列,实行先进先出的原则进行执行,先进入队列的任务优先执行; - 而
SynchronousQueue .TransferQueue
队列的先进先出的意思是:Worker
创建好并执行完任务之后,将Worker
线程放入队列,等待其它任务的提交,那么其它任务提交时,先进入队列的Worker
线程会被优先选择,用于执行任务; - 打个比方:
LinkedBlockingQueue
中,将Worker
比作厨师,Runnable
为顾客,LinkedBlockingQueue
的排队方式是,顾客排成一条长队,厨师们争先恐后地抢着为顾客服务,不过也要遵守规矩,依次从排在最前面的顾客挑选出来进行服务;SynchronousQueue
中,同样将Worker
比作厨师,Runnable
为顾客,但是SynchronousQueue
的排队方式是,将厨师拍成一条长队,顾客们你们随便来,来一个顾客我就把排最前面的厨师挑选出来,给你服务。