击碎java并发7JDK8巨人Executor及其王国
- 1 Executor简介
- 2 Executor组成
- 3 BlockingQueue
- 4 Executor类簇
- 4.1 Executor
- 4.2 ExecutorService
- 4.3 AbstractExecutorService
- 4.4 ThreadPoolExecutor
- 4.4.1 重要字段ctl
- 4.4.2 重要字段workQueue:
- 4.4.3 重要字段mainLock:
- 4.4.4 重要字段workers:
- 4.4.5 重要字段termination:
- 4.4.6 重要字段largestPoolSize:
- 4.4.7 重要字段completedTaskCount:
- 4.4.8 重要字段threadFactory
- 4.4.9 重要字段handler
- 4.4.10 重要字段keepAliveTime
- 4.4.11 重要字段allowCoreThreadTimeOut:
- 4.4.12 重要字段corePoolSize
- 4.4.13 重要字段maximumPoolSize
- 4.4.14 重要内部类Worker
- 4.4.14 重要内部类CallerRunsPolicy
- 4.4.15 重要内部类 AbortPolicy
- 4.4.16 重要的内部类DiscardPolicy
- 4.4.17 重要的内部类DiscardOldestPolicy
- 4.4.18 重要的方法execute
- 4.4.19 重要的方法addWorker
- 4.4.20 重要的方法addWorker
- 4.5 ForkJoinPool
- 4.6 ScheduledExecutorService
- 4.7 ScheduledThreadPoolExecutor
- 5 Future族簇
- 6 CompletionService
- 7 Executors
1 Executor简介
1.1 干嘛用的?
用来执行任务。这些任务实现了Runnable接口或者Callable接口。
1.2 有哪些好处?
1.重复利用线程,让工作线程去执行任务。
2.可以实现任务的取消,超时等操作。
3.让线程之间协调更加减单。比如主线程可以选择等待任务完成在工作,还是在以后某个再去获取任务完成的结果
1.3 工作原理
消费者: 一组工作线程,一直从任务队列中获取任务。获取不到则阻塞(当队列中有任务时,会被唤醒),获取到了则运行任务。
生产者:需要执行的任务的线程,把任务扔到队列中。队列满了则阻塞(当队列中任务不满时,会被唤醒),队列没满则扔任务成功,并立即获取一个可以控制任务的对象Future
2 Executor组成
2.1 Runnable和Callable
被执行的任务
2.2 Future
一个控制器,可以用来获取任务结果,或者取消任务,或者查看任务是否执行完成等等。常用的有:CompletableFuture,CountedCompleter, ForkJoinTask, FutureTask, RecursiveAction, RecursiveTask, SwingWorker
2.3 BlockingQueue
生产者和消费者工作的枢纽。用存放生产者生产的任务,用户存放消费者消费的任务。阻塞队列有助于简化开发,常用的阻塞队列有:ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue,DelayQueue, LinkedTransferQueue, SynchronousQueue
2.4 ThreadPool
消费者,本身包含着一定数量的工作线程,一直从队列中获取,并执行任务。常用的线程池有:ForkJoinPool,ScheduledThreadPoolExecutor,ThreadPoolExecutor。
3 BlockingQueue
3.1 BlockingQueue
阻塞队列的顶级接口,线程的阻塞是通过锁,主要提供以下几种功能。
- put方法:向队列中插入元素时,如果队列满了,则阻塞插入的线程。等到队列不满时,在唤醒插入线程,在次尝试插入。
- offer方法:待限时的put方法。
- take方法:从队列中取元素时,如果队列空了,则阻塞取的线程。等到队列中有元素时,在唤醒取线程,在次尝试获取。
- poll方法: 带时限的take方法。
3.2 ArrayBlockingQueue
是一个数组组成的队列。一把锁,两个条件。
3.2.1 重要字段items
用来存放元素的数组,居然还不是一个泛型。。。。
3.2.2 重要字段takeIndex和putIndex
分别用来指示队列的头元素位置和尾元素位置。
3.2.3 重要字段count
用来表示元素的数量
3.2.4 重要字段lock
主锁,所有的操作都被主锁保护,这个锁使用的是ReentrantLock。
3.2.5 重要字段notEmpty和notFull
空队列条件和满队列条件,分别用来在获取空队列时阻塞线程和插入满队列时阻塞线程。
3.2.5 其他
自行看代码吧,比较简单。
3.3 LinkedBlockingQueue
是一个单向链表组成的队列。两把锁,两个条件,注意对队列的全部遍历还是需要使用两把锁。通过count==0,消除了两把锁竞争的情况。
3.3.1 重要内部类Node
用来存放元素和构成链表的节点。
3.3.2 重要字段head和last
分别用来指示队列的头节点和尾节点。
3.3.3 重要字段count和capacity
用来表示元素的数量,和最大容量
3.3.4 重要字段takeLock和putLock
分别保护头结点和尾结点的锁。
3.3.5 重要字段notEmpty和notFull
空队列条件和满队列条件,分别用来在获取空队列时阻塞线程和插入满队列时阻塞线程。
3.3.6 其他
自行看代码吧,比较简单。
3.4 PriorityBlockingQueue
底层是用堆实现的,而堆又是由一个可以扩容的数组实现。线程的阻塞实现使用lock。自行看下把,比较简单,算是算法导论中优先级队列的典型应用。
3.3.1 重要字段DEFAULT_INITIAL_CAPACITY
数组默认的初始容量
3.3.2 重要字段MAX_ARRAY_SIZE
数组的最大长度
3.4.3 重要字段queue
保存元素的数组
3.4.4 重要字段size
保存元素的数量
3.4.5 重要字段comparator
优先级比较器,当他为null的时候,使用自然排序
3.4.6 重要字段lock
访问队列的锁
3.4.7 重要字段notEmpty
空队列条件,用来再获取空队列时阻塞线程。没有满队列条件,默认是无界的。
3.4.8 重要字段allocationSpinLock
扩容锁
3.5 DelayedWorkQueue
底层是用堆实现的,而堆又是由一个可以扩容的数组实现。线程的阻塞实现使用lock。自行看下把,比较简单,算是算法导论中优先级队列的典型应用。
3.6 SynchronousQueue
这是一个无锁队列。这里会仔细的看一下。这里会用到两个数据结构:双端队列,用来实现公平模式;双端栈,用来实现非公平模式。
无论是公平模式还是非公平模式下:每个一节点要么是空的,要么是代表着data(put方法提供的数据),要么是代表着request(存放take方法的请求)
因为put和take方法的对称性,put和take方法用了一个方法去实现。因为两者都是如下语意:找到匹配节点,则唤醒被匹配节点,自身操作成功,找不到匹配者,则新增节点,并阻塞自己。
3.6.1 公平模式
3.6.1.1 公平模式下的transfer方法
3.6.1.1.1 总体逻辑
总体上是一个自旋:无论是take还是put,要么从队列的尾结点插入,从要么队列的头结点进行匹配,循环地直到插入或者匹配成功。
而插入和匹配操作碰到中间状态时,要么帮助完成中间状态,要么碰到中间状态就从新自旋。
从代码的结构上来看还是非常清晰的
3.6.1.1.2 插入
如果是空的队列,或者队尾的模式相同,则插入。
获取末尾节点的下一个节点,如果节点不为空则说明,则说明末尾节点已经过期,帮助前进末尾节点(不管是否成功),并重新自旋。
如果超时,则失败。
如果待插入的节点等于空,则创建待插入的节点,防止因为自旋多次创建待插入的节点
更新先更新尾结点的下一个节点,然后在更新尾结点,这里的中间态分析,请参考AbstractSynchronizedQueue的enq方法的中间态分析,这实际上也是一种,CLHQueue。
然后调用awaitFulfill方法,阻塞线程,等待此节点被匹配上。
如果awaitFulfill方法,返回的是自己,说明该节点已经超时,调用clean方法,清除该节点。
如果该节点,已经被从Queue中移除了,则返回匹配到的数据,否者,移除该节点(该节点是头结点的情况下,同时会设置头结点的next节点为自己),并对该节点做相关的清理
3.6.1.1.3 匹配
插入条件不成立,则匹配。
获取头结点的下一个节点为填充节点。
如果栈中的头节点和尾结点和填充节点,已经过期,重新自旋
如果填充节点已经被填充过了或者填充节点已经被取消了或者填充节点失败,则前进头结点(同时会设置头结点的next节点为自己)并从新自旋,否则,前进头结点(同时会设置头结点的next节点为自己),并唤醒填充节点中的线程,后返回匹配到的数据
3.6.1.2 公平模式下的awaitFulfill方法
总体上来看是个限时的自旋(自旋的时间取决于cpu架构),如果超过了自旋时间,则会阻塞线程。
这里也是支持中断和超时的,被中断的和超时的节点会设置取消,最终调用clean方法清理。
3.6.1.3 公平模式下的clean方法
这里最终要实现 被取消的节点的next节点不在是当前节点即可。
从头结点开始,如果头结点的下一个节点被取消了,则前进头结点,并重新循环,这里是从头结点清理取消的节点。
如果队列已经空了,则结束清理。
如果刚刚获取的尾结点已经过期,则从新循环。
如果刚刚获取的尾结点的下一个节点不为空,则前进尾结点,并重新循环。
如果被取消的节点不是尾结点:如果尾结点的下一个节点等于自己(这里是被别的线程删除了被调用前进头结点时,会设置自己的下一个节点等于自己),或者是把前一个节点的下一个节点变成被取消节点成功则返回,否者重新循环
接下来是处理被取消的节点为尾结点的情况,这里不能直接前一个节点的下一个节点变成尾结点的下一个节点的原因:尾结点的下一个节点是既不是take方法,也不是put方法生成节点,没有真实意义。
如果cleanMe为空,并且cleanMe置成被取消节点的前一个节点成功(等待着被取消的节点不是尾结点,clean方法可以清理,或者在插入完成或者匹配完成时出队。),则clean成功。
如果cleanMe不为空,尝试清理cleanMe的下一个节点(把cleanMe的下一个节点变成被cleanMe的下一个节点的下一个节点),这里的诸多判断保证了碰到中间状态的过渡,要么帮助完成,要么重新清理,其他情况则重新自旋。
尝试清理成功以后,将cleanMe置成null,以后要么重新循环(把cleanMe设置被取消节点的前一个节点),要么该项任务已经成功就直接返回
3.6.1.4 公平模式小结
队列一旦初始化以后,总会有一个dummy节点,这就是CLH队列。
取消节点要么调用clean方法取消(这个是辅助的方法,能够快速删除),要么在插入或者匹配完成后自动出队(通过调用前进头节点或者前进尾结点)。
取消节点会把item设置为node(通过tryCancel方法)自己或者next设置为自己(通过调用前进头节点或者前进尾结点)
next节点是队列连接器,对next的写操作基本上都是用cas方式,通过调用前进头节点或者前进尾结点这两个方法有点特殊,他们避免的竞争。
插入总是在操作队尾,匹配总是在操作对头。
3.6.1.5 公平模式核心代码
/** Node class for TransferQueue. */
static final class QNode {
volatile QNode next; // next node in queue
volatile Object item; // CAS'ed to or from null
volatile Thread waiter; // to control park/unpark
final boolean isData;
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp &&
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
/**
* Tries to cancel by CAS'ing ref to this as item.
*/
void tryCancel(Object cmp) {
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
}
boolean isCancelled() {
return item == this;
}
/**
* Returns true if this node is known to be off the queue
* because its next pointer has been forgotten due to
* an advanceHead operation.
*/
boolean isOffList() {
return next == this;
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = QNode.class;
itemOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/**
* Puts or takes an item.
*/
@SuppressWarnings("unchecked")
E transfer(E e, boolean timed, long nanos) {
/* Basic algorithm is to loop trying to take either of
* two actions:
*
* 1. If queue apparently empty or holding same-mode nodes,
* try to add node to queue of waiters, wait to be
* fulfilled (or cancelled) and return matching item.
*
* 2. If queue apparently contains waiting items, and this
* call is of complementary mode, try to fulfill by CAS'ing
* item field of waiting node and dequeuing it, and then
* returning matching item.
*
* In each case, along the way, check for and try to help
* advance head and tail on behalf of other stalled/slow
* threads.
*
* The loop starts off with a null check guarding against
* seeing uninitialized head or tail values. This never
* happens in current SynchronousQueue, but could if
* callers held non-volatile/final ref to the
* transferer. The check is here anyway because it places
* null checks at top of loop, which is usually faster
* than having them implicitly interspersed.
*/
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
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
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;
}
}
}
/**
* Spins/blocks until node s is fulfilled.
*
* @param s the waiting node
* @param e the comparison value for checking match
* @param timed true if timed wait
* @param nanos timeout value
* @return matched item, or s if cancelled
*/
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);
}
}
/**
* Gets rid of cancelled node s with original predecessor pred.
*/
void clean(QNode pred, QNode s) {
s.waiter = null; // forget thread
/*
* At any given time, exactly one node on list cannot be
* deleted -- the last inserted node. To accommodate this,
* if we cannot delete s, we save its predecessor as
* "cleanMe", deleting the previously saved version
* first. At least one of node s or the node previously
* saved can always be deleted, so this always terminates.
*/
while (pred.next == s) { // Return early if already unlinked
QNode h = head;
QNode hn = h.next; // Absorb cancelled first node as head
if (hn != null && hn.isCancelled()) {
advanceHead(h, hn);
continue;
}
QNode t = tail; // Ensure consistent read for tail
if (t == h)
return;
QNode tn = t.next;
if (t != tail)
continue;
if (tn != null) {
advanceTail(t, tn);
continue;
}
if (s != t) { // If not tail, try to unsplice
QNode sn = s.next;
if (sn == s || pred.casNext(s, sn))
return;
}
QNode dp = cleanMe;
if (dp != null) { // Try unlinking previous cancelled node
QNode d = dp.next;
QNode dn;
if (d == null || // d is gone or
d == dp || // d is off list or
!d.isCancelled() || // d not cancelled or
(d != t && // d not tail and
(dn = d.next) != null && // has successor
dn != d && // that is on list
dp.casNext(d, dn))) // d unspliced
casCleanMe(dp, null);
if (dp == pred)
return; // s is already saved node
} else if (casCleanMe(null, pred))
return; // Postpone cleaning s
}
}
3.6.2 非公平模式
3.6.2.1 非公平模式下的transfer方法
3.6.2.1.1 总体逻辑
总体上是一个自旋:无论是take还是put,总是在栈顶插入或者匹配,中间态被单独提出来了,与插入或者匹配并行,循环地直到插入或者匹配成功,匹配总是和栈顶的相匹配。跟Queue的方法总体结构上有一定的差别,可能是不同的人写的。
从代码的结构主体上来看还是非常清晰的,代码复用率提高了。
3.6.2.1.2 插入
如果是空栈,或者栈顶的模式相同,则插入。
如果已经超时,如果栈顶被取消,尝试pop栈顶,并重新自旋,否则插入失败。
如果新生成节点或者复用节点,插入栈顶成功,则调用awaitFulfill方法,阻塞线程,等待此节点被匹配上。
如果awaitFulfill方法,返回的是自己,说明该节点已经超时,调用clean方法,清除该节点。
如果该节点是栈顶节点的下一个节点,则更新栈顶节点为插入节点的下一个节点,并返回匹配节点的数据。
其他情况从新自旋
3.6.2.1.3 匹配
插入条件不成立,并且栈顶没有被完全填充,则尝试进行匹配。
如果栈顶已经被取消了。如果栈顶被取消,尝试pop栈顶,并重新自旋。
如果新生成节点或者复用节点,插入栈顶成功,则进入匹配自旋,直到,匹配成功,或者没有可以匹配者重新自旋。
如果没有可以匹配的节点,则跳出匹配自旋,并重新自旋
如果匹配成功则弹出插入的节点和他匹配的节点,并返回匹配节点的数据。
如果匹配不成功重置插入节点的下一个节点为下下一个节点,并从新匹配自旋。
3.6.2.1.4 中间态
主要帮助进行匹配,和自旋匹配比较相似。
3.6.2.2 非公平模式下的awaitFulfill方法
总体上来看是个限时的自旋(自旋的时间取决于cpu架构),如果超过了自旋时间,则会阻塞线程。
这里也是支持中断和超时的,被中断的和超时的节点会设置取消,最终调用clean方法清理。
3.6.2.3 非公平模式下的clean方法
获取被取消节点的下一个节点,如果下一个节点被取消了,则再获取下一个节点,为什么不循环到栈底,即取消被取消的节点到栈底之间的取消节点。
从栈顶开始遍历,取消碰到的被取消的节点,直到碰到本次被取消的节点或者不被取消的节点。
如果上一步遍历到的节点不是本次取消的节点,继续遍历取消中间被取消的节点,直到碰到本次被取消的节点。
3.6.2.4 非公平模式小结:
栈一旦初始化以后,栈顶可以变化为null
取消节点要么调用clean方法取消(这个是辅助的方法,能够快速删除),要么在插入或者匹配完成后自动出栈(通过调用更改栈顶方法)。
取消节点会把match设置为node(通过tryCancel方法)自己
next节点是栈连接器,对next的写操作基本上都是用cas方式,但是却不用关心该操作是否成功与否,因为如果自己失败了,其他线程会帮助其完成。
插入总是在栈顶,匹配会插入新的栈顶,并去匹配老的栈顶,最终弹出新栈顶和老栈顶。
3.6.1.5 非公平模式核心代码
/** Node class for TransferStacks. */
static final class SNode {
volatile SNode next; // next node in stack
volatile SNode match; // the node matched to this
volatile Thread waiter; // to control park/unpark
Object item; // data; or null for REQUESTs
int mode;
// Note: item and mode fields don't need to be volatile
// since they are always written before, and read after,
// other volatile/atomic operations.
SNode(Object item) {
this.item = item;
}
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
/**
* Tries to match node s to this node, if so, waking up thread.
* Fulfillers call tryMatch to identify their waiters.
* Waiters block until they have been matched.
*
* @param s the node to match
* @return true if successfully matched to 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;
}
/**
* Tries to cancel a wait by matching node to itself.
*/
void tryCancel() {
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
boolean isCancelled() {
return match == this;
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long matchOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = SNode.class;
matchOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("match"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/**
* Puts or takes an item.
*/
@SuppressWarnings("unchecked")
E transfer(E e, boolean timed, long nanos) {
/*
* Basic algorithm is to loop trying one of three actions:
*
* 1. If apparently empty or already containing nodes of same
* mode, try to push node on stack and wait for a match,
* returning it, or null if cancelled.
*
* 2. If apparently containing node of complementary mode,
* try to push a fulfilling node on to stack, match
* with corresponding waiting node, pop both from
* stack, and return matched item. The matching or
* unlinking might not actually be necessary because of
* other threads performing action 3:
*
* 3. If top of stack already holds another fulfilling node,
* help it out by doing its match and/or pop
* operations, and then continue. The code for helping
* is essentially the same as for fulfilling, except
* that it doesn't return the item.
*/
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;
} else if (casHead(h, s = snode(s, e, h, mode))) {
SNode m = awaitFulfill(s, timed, nanos);
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
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
}
}
}
}
/**
* Spins/blocks until node s is matched by a fulfill operation.
*
* @param s the waiting node
* @param timed true if timed wait
* @param nanos timeout value
* @return matched node, or s if cancelled
*/
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
/*
* When a node/thread is about to block, it sets its waiter
* field and then rechecks state at least one more time
* before actually parking, thus covering race vs
* fulfiller noticing that waiter is non-null so should be
* woken.
*
* When invoked by nodes that appear at the point of call
* to be at the head of the stack, calls to park are
* preceded by spins to avoid blocking when producers and
* consumers are arriving very close in time. This can
* happen enough to bother only on multiprocessors.
*
* The order of checks for returning out of main loop
* reflects fact that interrupts have precedence over
* normal returns, which have precedence over
* timeouts. (So, on timeout, one last check for match is
* done before giving up.) Except that calls from untimed
* SynchronousQueue.{poll/offer} don't check interrupts
* and don't wait at all, so are trapped in transfer
* method rather than calling awaitFulfill.
*/
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);
}
}
/**
* Unlinks s from the stack.
*/
void clean(SNode s) {
s.item = null; // forget item
s.waiter = null; // forget thread
/*
* At worst we may need to traverse entire stack to unlink
* s. If there are multiple concurrent calls to clean, we
* might not see s if another thread has already removed
* it. But we can stop when we see any node known to
* follow s. We use s.next unless it too is cancelled, in
* which case we try the node one past. We don't check any
* further because we don't want to doubly traverse just to
* find sentinel.
*/
SNode past = s.next;
if (past != null && past.isCancelled())
past = past.next;
// Absorb cancelled nodes at head
SNode p;
while ((p = head) != null && p != past && p.isCancelled())
casHead(p, p.next);
// Unsplice embedded nodes
while (p != null && p != past) {
SNode n = p.next;
if (n != null && n.isCancelled())
p.casNext(n, n.next);
else
p = n;
}
}
3.7 LinkedTransferQueue
公平模式的SynchronousQueue,只不过,支持立即,异步,同步,限时,操作。实现上更加干练一些。
立即:先匹配,匹配成功立即返回;匹配不成功立即返回,不会插入节点
异步:先匹配,匹配成功立即返回;匹配不成功会插入节点后立即返回
同步:先匹配,匹配成功立即返回:匹配不成功会插入节点后,阻塞当前线程并等待匹配唤醒。
限时:先匹配,匹配成功立即返回:匹配不成功会插入节点后,阻塞当前线程并等待匹配唤醒或者超时自动取消。
3.8 DelayQueue
内部实现通过ReentrantLock和PriorityQueue
3.9 非阻塞代码阅读与分析小结与展望
3.9.1 阅读
非租塞队列的阅读耗费了我大量的时间,也包括之前的AbstractSynchronizedQueue的非租塞部分。
3.9.2 分析与小结
从一个稳定的正确终态过度到一个另外一个稳定的正确的终态,变动的变量越多,操作步骤越多,中间状态也就越多,考察代码的完备性也就会越难。
如果能够换一种思路能够以某种数学方面的形式来表示,比如:公式,图表,就好了。只是一个想法。这样在不同的步骤之间思考中间状态的影响就会容易的多,不用再那么费神。
更甚者如果能够归纳出通性,用数学来解决,那非租塞算法将会变得简单。就我目前看来,这应该是会是一个从时间维度来说是一个错乱的多元方程。
3.9.3 展望
有没有一种以通用的数学上的办法去设计,产生,验证非带塞代码?
有一系列的状态:终止状态(线程安全),中间状态(线程不安全)。
每个状态,由一些元素组成。
每个元素又是有值的。
一个状态可以变化到另外一个状态,通过改变元素的值。
代码可以被看成是一张由状态组成的图。
图中有一个起始状态,和多个结束状态。
上面的问题就可以被转化成:
在给定元素,每个元素值的集合,终止状态,中间状态的条件下:
是否有一张图,从起点状态变化到结束状态,对于任意的起点状态(起点状态为终止状态)变化到结束状态后,所有的结束状态都是终止状态?
4 Executor类簇
4.1 Executor
executor框架的顶级父类。仅有一个方法只能支持执行runable的任务。
4.2 ExecutorService
继承于Executor,提供了关闭,立即关闭,执行callable返回future和执行runable返回future,以及执行任务列表的功能。
4.3 AbstractExecutorService
继承于ExecutorService,提供了一个把runable和callable任务打包成FutureTask方法。(最终会使用适配器模式把runable变成callable,即call方法内执行run方法)
实现了执行单个任务,最终会调用execute方法。
实现了执行任务列表,最终会调用doInvokeAny私有方法。
4.3.1 重要方法newTaskFor
这个方法主要作用在于吧Callable和runnable转成FutureTask。
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
4.3.2 重要方法newTaskFor
该方法通过调用CompletionService来执行,因为CompletionService更加适合执行批量任务,后面会介绍。
/**
* the main mechanics of invokeAny.
*/
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null)
throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0)
throw new IllegalArgumentException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
// For efficiency, especially in executors with limited
// parallelism, check to see if previously submitted tasks are
// done before submitting more of them. This interleaving
// plus the exception mechanics account for messiness of main
// loop.
try {
// Record exceptions so that if we fail to obtain any
// result, we can throw the last exception we got.
ExecutionException ee = null;
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Iterator<? extends Callable<T>> it = tasks.iterator();
// Start one task for sure; the rest incrementally
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1;
for (;;) {
Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
else
f = ecs.take();
}
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
4.4 ThreadPoolExecutor
继承于ExecutorService是最长用的线程池之一。
4.4.1 重要字段ctl
用来表示工作线程的数量,最高位三位标识执行器的状态,状态有以下几种:
运行中,关闭(调用shutdown方法),停止(调用shutdownNow方法),清理中(清空任务队列里,和工作线程),终止。
4.4.2 重要字段workQueue:
用来存放未被执行的。
4.4.3 重要字段mainLock:
这把锁有助于对工作线程的访问,以及工作线程相关的。
4.4.4 重要字段workers:
用来存放工作线程,是个set
4.4.5 重要字段termination:
mainLock的条件,主要用来等待服务终止
4.4.6 重要字段largestPoolSize:
出现的最大的工作线程的数量,仅是一个记录值
4.4.7 重要字段completedTaskCount:
已完成的任务的数量。工作线程终止时会更新该计数器。
4.4.8 重要字段threadFactory
产生工作线程的工厂,这里应用了工厂模式。
4.4.9 重要字段handler
拒绝执行任务的策略,这里应用了策略模式,和空对象模式。
4.4.10 重要字段keepAliveTime
工作线程任务的超时时间
4.4.11 重要字段allowCoreThreadTimeOut:
是否使用keepAliveTime,使用时,获取任务超时,工作线程终止。
4.4.12 重要字段corePoolSize
最少的存活的工作线程的数量
4.4.13 重要字段maximumPoolSize
最大的工作线程数量
4.4.14 重要内部类Worker
工作线程继承了AbstractSynchronizedQueue,实现了Runnable。
内部有三个字段:
thread:当前worker所在线程,而我总是习惯他们说成一样的。
firstTask:执行的第一个任务,这task就有点类似锁中的非公平模式。
completedTasks: 当前线程执行任务的数量
一个方法:
run:调用runWorker方法,传参为自己。这个地方的代码让我忍不住想起了阅读javac源码时的访问者模式。
4.4.14 重要内部类CallerRunsPolicy
拒绝执行任务的策略:让调用者自己执行。最终执行任务的是,抛出任务的线程,也就是调用线程,不是工作线程。
4.4.15 重要内部类 AbortPolicy
拒绝执行任务的策略:不执行任务,直接抛出异常,这是默认的策略
4.4.16 重要的内部类DiscardPolicy
拒绝执行任务的策略:不执行任务。慎用!
4.4.17 重要的内部类DiscardOldestPolicy
拒绝执行任务的策略:丢弃最老任务,重新执行最新的任务。慎用!
4.4.18 重要的方法execute
真正执行任务地方。
如果任务为空,直接抛出异常。(为什么第一步,不是判断状态呢?)
如果当前工作线程数量小于最少的存活的工作线程,尝试调用addWorker方法,增加工作线程,如果工作线程增加成功,则结束
如果当前服务正在运行,并且任务入队成功:
再次检查ctl
如果当前服务已经停止了调用remove方法成功后,调用reject拒绝任务。
如果当前工作线程已经为0,调用addWorker方法,增加工作线程。
如果当前服务不在运行,调用reject拒绝任务。
4.4.19 重要的方法addWorker
retry的双从死循环。
外层判断服务是否被关闭:如果被关闭了,增加worker失败。
内存循环判断worker是否达到上限,(最少线程数或者最大线程数),达到上限后悔增加Worker失败。
cas增加工作ctl成功则退出双从循环,cas失败了则会从新从外层开始继续执行。
ctl的增加看起来像是使用预占位置模式。
接下来创建Worker,创建Worker同时会创建工作线程。
通过主锁,把worker放到workers中。
如果workers的数量高于曾经记录的值更新largestPoolSize。
worker增加成功以后,启动Worker内部的线程,worker内部的线程启动时,会调用worker的run方法,run方法又会调用runWoker方法,这个方法后面介绍。
如果工作线程启动失败了,调用addWorkerFailed方法。该方法,会通过主锁,从workers中移除线worker,减少ctl,并调用tryTerminate方法,尝试终止服务。tryTerminate方法,会通过主锁,设置ctl,并唤醒,等待获取,主锁的线程,这里并不会中断工作线程。
/**
* 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 &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
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
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new 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)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
/**
* 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
*/
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
/**
* 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.
*/
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
}
}
4.4.20 重要的方法addWorker
工作线程启动以后,如果带有初始化任务,会执行初始化任务。
没有初始化任务会通过调用getTask方法从任务队里中获取任务,调用任务的run方法。
如果任务执行以后,更新任务的增加worker完成任务的计数器。
而后继续获取任务,执行任务的循环。
如果在执行任务的过程中发生了异常或者被中断:调用processWorkerExit方法,执行工作线程退出。该方法会把该线程执行过的任务数量,累加到completedTaskCount上。并更根据ctl决定是否调用addWorker方法。
/**
* Main worker run loop. Repeatedly gets tasks from queue and
* executes them, while coping with a number of issues:
*
* 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.
*
* 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.
*
* 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.
*
* 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.
*
* 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.
*
* 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 {
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);
}
}
/**
* Performs cleanup and bookkeeping for a dying worker. Called
* only from worker threads. Unless completedAbruptly is set,
* assumes that workerCount has already been adjusted to account
* for exit. This method removes thread from worker set, and
* possibly terminates the pool or replaces the worker if either
* it exited due to user task exception or if fewer than
* corePoolSize workers are running or queue is non-empty but
* there are no workers.
*
* @param w the worker
* @param completedAbruptly if the worker died due to user exception
*/
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
4.5 ForkJoinPool
是ThreadPoolExecutor的多个工作队列版本。核心思想:有多个队列,如果一个线程已经把自己的队列内的任务执行完了,去别的队列中去取任务。在cpu计算密集型场景下表现很好。
4.6 ScheduledExecutorService
继承于ExecutorService,提供了一些限时执行任务的方法。
4.7 ScheduledThreadPoolExecutor
继承于ScheduledExecutorService,是它实现类。
5 Future族簇
5.1 Future
顶级父接口。future主要用来控制任务。比如获取任务的执行的结果(支持限时),取消任务,查询任务是否完成。
5.2 RunnableFuture
继承了Runnable和Future。
5.3 FutureTask
future的重要实现,主要用在ThredPoolExecutor中。
5.3.1 重要字段state
用来表示任务执行的状态。
有创建状态,任务完成状态,普通状态,异常状态,取消状态,正在被中断状态,已经被中断状态。
5.3.2 重要字段callable
我们自己调用executor的执行方法,时传递的callable或者,runnable的适配器。
5.3.3 重要字段outcome
用来存放callable执行的结果,或者runnable传入的结果。
5.3.4 重要字段runner
执行任务的工作线程
5.3.5 重要字段waiters
等待任务的任务完成的队列是单向链表
5.3.6 重要构造方法FutureTask
在AbstractExecutorService的newTaskFor方法调用的就是此方法。
5.3.7 重要内部类WaitNode
等待任务完成的队列的节点。主要有threa和next两个字段
/**
* Simple linked list nodes to record waiting threads in a Treiber
* stack. See other classes such as Phaser and SynchronousQueue
* for more detailed explanation.
*/
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
5.3.8 重要方法run
ThreadPoolExecutor的的runWorker的task.run,调用就是此方法(如果是他实现的话)
调用callable的call方法真正的执行任务(runnable的适配器,会在内部调用run方法)
任务执行完成以后设置任务为完成状态,并设置结果,设置结果以后设置任务为普通状态。并通过set方法调用finishCompletion方法,唤醒在队列中等待的线程。
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);
}
}
/**
* 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();
}
}
/**
* 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
}
5.3.9 重要方法get
主要用来任务执行的结果。如果任务未完成调用awaitDone方法,该方法是个典型的cas代码,不过多解释,阻塞获取线程。任务完成以后,会被唤醒,并通过report方法返回任务执行的结果。
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
/**
* 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);
}
/**
* Returns result or throws exception for completed task.
*
* @param s completed state value
*/
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
5.4 其他
类似
6 CompletionService
类似ExecutorService。但是它更适合执行异步批量任务,future的get方法,会返回最先执行完成的任务的结果。
其实现类为:ExecutorCompletionService。
7 Executors
一个辅助类。可以辅助创建ExecutorService。