我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码
文章目录
- 一、前言
- 二、架构
- 2.1 UML 图
- 2.2 TransferStack流程图
- 2.2.1 节点匹配流程图
- 2.2.2 transfer 流程图
- 2.2.3 awaitFulfill 流程图
- 2.3 TransferQueue流程图
- 2.3.1 节点匹配流程图
- 2.3.2 TransferQueue#transfer 流程图
- 2.3.3 TransferQueue#clean 流程图
- 2.4 特性
- 2.5 SynchronousQueue 与 AbstractQueuedSynchronizer 的关系
- 三、SynchronousQueue 剖析
- 3.1 构造方法
- 3.2 take 方法 (取元素)
- 3.3 put 方法 (插元素)
- 3.4 内部静态抽象类 Transferer
- 3.5 take 与 put 内部区别
- 3.6 变量 cleanMe
- 四、 TransferStack 栈(队列出入非公平,先进后出,常用)
- 4.1 SNode 节点
- 4.2 transfer 方法
- transfer 流程图
- 4.3 casHead 方法
- 4.4 isCancelled 方法
- 4.5 isFulfilling 方法
- 4.6 tryMatch 方法
- 4.7 awaitFulfill 方法
- awaitFulfill 流程图
- 4.8 shouldSpin 方法
- 4.9 maxTimedSpins 变量与 maxUntimedSpins 变量
- 4.10 spinForTimeoutThreshold 变量
- 4.11 tryCancel 方法
- 4.12 clean 方法
- 五、TransferQueue 栈(队列出入公平,先进先出)
- 5.1 QNode 节点
- 5.2 transfer 方法
- transfer 流程图
- 5.3 casItem 方法 (重要)
- 5.4 clean 方法
- 5.5 casCleanMe 方法
- 六、总结
- 七、番外篇
public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -3223113410248163686L;
=== 点击查看top目录 ===
private transient volatile Transferer<E> transferer;
public SynchronousQueue() {
this(false);
}
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
=== 点击查看top目录 ===
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
=== 点击查看top目录 ===
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
=== 点击查看top目录 ===
abstract static class Transferer<E> {
abstract E transfer(E e, boolean timed, long nanos);
}
=== 点击查看top目录 ===
两者底层都是调用了 transferer.transfer(E e, boolean timed, long nanos); 方法,区别在于:
=== 点击查看top目录 ===
transient volatile QNode cleanMe;
static final class SNode {
// next指向栈中下一个元素
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;
=== 点击查看top目录 ===
E transfer(E e, boolean timed, long nanos) {
// 既然是 queue 队列,内部肯定都是用 Node 包装
SNode s = null;
/*
mode 模式(操作):
若是 null,那么是 take 操作,也就是 REQUES
若是元素 e,那么是 put 操作,也就是 DATA
*/
int mode = (e == null) ? REQUEST : DATA;
for (;;) {
// 这个 h = head, 一开始肯定是 null
SNode h = head;
// 要么 head 是 null,要么队列中 mode一样,全都是 take 或者全都是 put,进入下面的 if
if (h == null || h.mode == mode) { // empty or same-mode
/*
timed = true :表示如果有规定超时 timeout 时间。
nanos <= 0 : 表示到点了,该回家了
*/
if (timed && nanos <= 0) { // can't wait
if (h != null && h.isCancelled())
// 节点已经timeout 取消了,pop出 head节点
casHead(h, h.next); // pop cancelled node
else
return null;
/*
put 的时候,把 element 塞到 head 的位置(往头部插入)
cas 设置头节点 head 是 s (s节点包装了入queue 的 e)
*/
} else if (casHead(h, s = snode(s, e, h, mode))) {
/*
等待结束,也就是put的时候,阻塞在这里等被 take,
也就是 take 的时候,阻塞在这里等被 put.
这个方法重点讲。
// 通过awaitFulfill方法自旋阻塞找到匹配操作的节点
*/
SNode m = awaitFulfill(s, timed, nanos);
if (m == s) { // wait was cancelled,说明已经超时退出了
clean(s); // 清理 s 节点
return null; // 返回 null
}
if ((h = head) != null && h.next == s)
casHead(h, s.next); // help s's fulfiller
return (E) ((mode == REQUEST) ? m.item : s.item);
}
/*
还未完成任务 fulfill
能跑到这个if,说明head 队列是 take,突然来了个 put ,或者head 队列是 put,突然来了个 take
*/
} else if (!isFulfilling(h.mode)) { // try to fulfill
if (h.isCancelled()) // already cancelled
casHead(h, h.next); // pop and retry
/*
这个地方如果是取 take 的话,那么 s节点内是 null
首先把 take 的 Node 插入 head,然后此时 head 后面假如有 2个 put 的Node阻塞着。
*/
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
for (;;) { // loop until matched or waiters disappear
// 接上面,这个地方 m = s.next ,也就是阻塞的 put Node
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
}
// mn 是下一个 Node
SNode mn = m.next;
// 尝试匹配,也就是一个 put 匹配一个 take,匹配上的话,俩俩牵走。匹配成功进入
if (m.tryMatch(s)) {
// 把 s 和 m 都拿掉,因为他们牵走走了。所以把 head 设置成 mn,也就是第3个
casHead(s, mn); // pop both s and m
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match
/*
如果匹配不成功,那么拿掉m节点。继续进入 for
匹配不成功是因为在那一瞬间被别人抢走了
*/
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
}
}
}
}
=== 点击查看top目录 ===
boolean casHead(SNode h, SNode nh) {
return h == head &&
UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
}
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
=== 点击查看top目录 ===
boolean isCancelled() {
return match == this;
}
=== 点击查看top目录 ===
/** Node represents an unfulfilled consumer */
static final int REQUEST = 0;
/**Node represents an unfulfilled producer */
static final int DATA = 1;
/** Node is fulfilling another unfulfilled DATA or REQUEST */
static final int FULFILLING = 2;
static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }
=== 点击查看top目录 ===
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置空
waiter = null;
LockSupport.unpark(w);
}
return true;
}
// 判断当前节点是否已与s进行匹配
return match == s;
}
=== 点击查看top目录 ===
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
// 获取超时时间点
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 当前线程
Thread w = Thread.currentThread();
// shouldSpin判断是否需要进行自旋
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
// 判断当前线程是否中断,外部中断操作,相当于取消本次操作
if (w.isInterrupted())
// 尝试将s节点的match设置为s自己,这样判断的时候就知道这个节点是被取消的
s.tryCancel();
SNode m = s.match;
// match非空则表示当前节点已经被匹配match匹配上
if (m != null)
return m;
// 超时配置处理
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
s.tryCancel();
continue;
}
}
// 自旋spins
if (spins > 0)
spins = shouldSpin(s) ? (spins-1) : 0;
// 设置等待线程
else if (s.waiter == null)
s.waiter = w;
// 未设置超时,直接阻塞
else if (!timed)
LockSupport.park(this);
// 设置超时时间阻塞
// 阻塞时间是否超过1000nm纳秒?如果是的话,一直自旋台耗CPU了,还是老老实实去睡觉吧。
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
=== 点击查看top目录 ===
boolean shouldSpin(SNode s) {
SNode h = head;
return (h == s || h == null || isFulfilling(h.mode));
}
=== 点击查看top目录 ===
static final int NCPUS = Runtime.getRuntime().availableProcessors();
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
static final int maxUntimedSpins = maxTimedSpins * 16;
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
- 如果不需要自旋,那么直接 spins = 0
- 如果需要自旋,并且会超时,那么 spins = maxTimedSpins = 32
- 如果需要自旋,并且不会超时,那么 spins = maxUntimedSpins = 512
=== 点击查看top目录 ===
static final long spinForTimeoutThreshold = 1000L;
...
// 超时配置处理
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
s.tryCancel();
continue;
}
}
...
// 自旋spins
if (spins > 0)
spins = shouldSpin(s) ? (spins-1) : 0;
// 未设置超时,直接阻塞
else if (!timed)
LockSupport.park(this);
// 设置超时时间阻塞
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
...
=== 点击查看top目录 ===
void tryCancel() {
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
/**
* Unlinks s from the stack.
*/
void clean(SNode s) {
// item,waiter 置空
s.item = null; // forget item
s.waiter = null; // forget thread
// s的下一个节点处于取消操作状态,则past指向past的下一个节点
SNode past = s.next;
if (past != null && past.isCancelled())
past = past.next;
// 头节点被取消操作则进行将next节点更新为头节点
/*
* 这里说一下为什么要:p != past ,因为如果 past == p = head的话,
* past就是头节点head,那么待会直接走下面的 while 就行,不用在这里浪费时间,直接跳过这一步。
*/
SNode p;
while ((p = head) != null && p != past && p.isCancelled())
casHead(p, p.next);
// 头节点调整完毕,现在将栈节点中每个节点都会进行检查一遍,更新前后节点的关系,将取消操作的节点进行排除
while (p != null && p != past) {
SNode n = p.next;
if (n != null && n.isCancelled())
p.casNext(n, n.next);
else
p = n;
}
}
=== 点击查看top目录 ===
//队列节点定义
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;
}
E transfer(E e, boolean timed, long nanos) {
// 注意,queue结构的是 QNode,Stack结构的是 SNode
QNode s = null; // constructed/reused as needed
// 判断操作是 take 还是 put
boolean isData = (e != null);
for (;;) {
QNode t = tail;
QNode h = head;
// 初始化 TransferQueue 的时候, head 与 tail 非 null
if (t == null || h == null) // saw uninitialized value
continue; // spin
// h == t 说明队列里面压根就没元素,只有一个假节点 QNode h = new QNode(null, false);
// t.isData == isData 说明队列内的元素与新进来的类型一致。记住是 tail
if (h == t || t.isData == isData) { // empty or same-mode
QNode tn = t.next;
// 确保 t 没问题
if (t != tail) // inconsistent read
continue;
// tn 按理来说必须是 null,因为 tail.next 尾巴之后肯定没元素了,如果此时有,那么就是刚插进来的
if (tn != null) { // lagging tail
// CAS 把 tail 指向最新的尾巴 tn 节点,
advanceTail(t, tn);
continue;
}
// 已经 timeout 超时了
if (timed && nanos <= 0) // can't wait
return null;
// s 第一次 for 进来就是 null,那么此时包装成节点Node
if (s == null)
s = new QNode(e, isData);
// 设置 t.next 是新进来的这个 Node
if (!t.casNext(null, s)) // failed to link in
continue; // CAS 失败就 continue,这个地方不加lock 和 Synchronized,利用了 CAS 其实挺省性能的
// ===== 前面的全部都是校验检查工作 =====
// === 下面的才是对新进来节点的处理 ===
// 将新进来的 NodeS 插入到队列的尾巴里面去
advanceTail(t, s); // swing tail and wait
// 一般来说,在这里会进入阻塞
Object x = awaitFulfill(s, e, timed, nanos);
// 如果由于超时timeout等情况被取消,那么会进入下面的 if
if (x == s) { // wait was cancelled
clean(t, s);
return null;
}
//匹配的操作到来,s操作完成,离开队列,如果没离开,使其离开(advanceHead 方法会使得 h.next = h 使得 s.isOffList() 为 true)
// 判断s是否被摘掉了 (s.next =this 说明被摘掉了,也就是匹配完毕了)
if (!s.isOffList()) { // not already unlinked
// 推进head ,cas将head 由t(是t不是h),设置为s
// head 设置成 s, 摘掉 t
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
//不同的模式那么是匹配的(put 匹配take ,take匹配put),出队操作
//队头元素节点
QNode m = h.next; // node to fulfill
//队列发生变化,重来
if (t != tail || m == null || h != head)
continue; // inconsistent read
Object x = m.item;
// 1. 如果isData == (x != null) 为true 那么表示是相同的操作。
//(x!= null为true说明队列第一个元素是 put 操作, isData=true 说明是 put 操作。 x!= null 为 false 说明第一个元素是 take 操作,isData=false说明是 take 操作 )
//其实能进入到上面的if,然后在这个地方出现了这种情况,原因就是多线程条件下,这个线程慢了,导致队列中状态都变了
// 2. x == m 则是被取消了的操作
//m.casItem(x, e) 将 m 的 item 设置为本次操作的 e,实际上这是最重要的匹配操作,如果失败,那么推进 head ,重试
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
//推进head
advanceHead(h, m); // dequeue and retry
continue;
}
//m.casItem 成功后,也要推进head
advanceHead(h, m); // successfully fulfilled
//唤醒匹配操作的线程
LockSupport.unpark(m.waiter);
return (x != null) ? (E)x : e;
}
}
}
advanceTail(t, tn); 这个是TransferQueue 区别与 TransferStack的一个根据,这个是从尾巴插入,从头部取,所以是先进先出。而 TransferStack 是从头部插入,头部读取。后进先出。
最重要的细节是:
=== 点击查看top目录 ===
boolean casItem(Object cmp, Object val) {
return item == cmp &&
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
/**
* Gets rid of cancelled node s with original predecessor pred.
* 对 中断的 或 等待超时的 节点进行清除操作
*/
void clean(QNode pred, QNode s) {
// 1. 清除掉 thread 引用
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.
*
* 在程序运行中的任何时刻, 最后插入的节点不能被删除(这里的删除指 通过 cas 直接删除, 因为这样直接删除会有多删除其他节点的风险)
* 当 节点 s 是最后一个节点时, 将 s.pred 保存为 cleamMe 节点, 下次再进行清除操作
*/
// 2. 判断 pred.next == s, 下面的 步骤2 可能导致 pred.next = next
while (pred.next == s) { // Return early if already unlinked
QNode h = head;
QNode hn = h.next; // Absorb cancelled first node as head
// 3. hn 中断或者超时, 则推进 head 指针, 若这时 h 是 pred 则 loop 中的条件 "pred.next == s" 不满足, 退出 loop
if (hn != null && hn.isCancelled()) {
advanceHead(h, hn);
continue;
}
QNode t = tail; // Ensure consistent read for tail
// 4. 队列为空, 说明其他的线程进行操作, 删除了节点(注意这里永远会有个 dummy node)
if (t == h)
return;
QNode tn = t.next;
// 5. 其他的线程改变了 tail, continue 重新来
if (t != tail)
continue;
if (tn != null) {
// 6. 帮助推进 tail
advanceTail(t, tn);
continue;
}
// 7. 节点 s 不是尾节点, 则 直接 CAS 删除节点(在队列中间进行这种删除是没有风险的)
if (s != t) { // If not tail, try to unsplice
QNode sn = s.next;
if (sn == s || pred.casNext(s, sn)) // 退出循环
return;
}
// 8. s 是队列的尾节点, 则 cleanMe 出场
QNode dp = cleanMe;
if (dp != null) { // Try unlinking previous cancelled node
// 9. cleanMe 不为 null, 进行删除删一次的 s节点, 也就是这里的节点d
QNode d = dp.next;
QNode dn;
// 10. 这里有几个特殊情况:
// 1. 原来的s节点()也就是这里的节点d已经删除;
// 2. 原来的节点 cleanMe 已经通过 advanceHead 进行删除;
// 3 原来的节点 s已经删除 (所以 !d.siCancelled), 存在这三种情况, 直接将 cleanMe 清除
if (d == null || // d is gone or
d == dp || // d is off list or
!d.isCancelled() || // d not cancelled or
// 11. d 不是tail节点, 且dn没有offlist, 直接通过 cas 删除 上次的节点 s (也就是这里的节点d); 其实就是根据 cleanMe 来清除队列中间的节点
(d != t && // d not tail and
(dn = d.next) != null && // has successor
dn != d && // that is on list
dp.casNext(d, dn))) // d unspliced
// 12. 清除 cleanMe 节点, 这里的 dp == pred 若成立, 说明清除节点s, 成功, 直接 return, 不然的话要再次 loop, 接着到 步骤 13, 设置这次的 cleanMe 然后再返回
casCleanMe(dp, null);
if (dp == pred)
return; // s is already saved node
// 原来的 cleanMe 是 null, 则将 pred 标记为 cleamMe 为下次 清除 s 节点做标识
} else if (casCleanMe(null, pred))
return; // Postpone cleaning s
}
}
这时我们想起了 ConcurrentSkipListMap 中的 marker 节点, 对, marker 和 cleanMe 都是起着防止并发环境中多删除节点的功能
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp &&
UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
}
=== 点击查看top目录 ===
上一章节:【Java Collection】Queue 剖析(四)