SynchronousQueue是一个典型的生产者与消费者队列,当一个线程A向队列中放入数据,除非其它线程将数据取走,否则线程A将一直阻塞。也就是说一个put操作渴望一个take操作,同样一个take操作渴望一个put操作。
同步队列分为公平与非公平模式,公平模式下遵循先进先出的原则基于队列结构。非公平模式则使用后进先出的方式,同栈结构。
同步队列实现的这两种模式,是通过实现内部接口abstract static class Transferer<E>来分别完成。
公平模式使用TransferQueue类实现,非公平模式使用TransferStack类实现,具体的处理逻辑在接口的
transfer(E e, boolean timed, long nanos)方法中完成。
transfer方法集put和take操作于一身,区别在于put:e是要放的元素,take:e是null。
public E take() throws InterruptedException {
//调用接口Transferer实现类的transferer方法,null代表take操作
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
//调用接口Transferer实现类的transferer方法,传入e
if (transferer.transfer(e, false, 0) == null) {
Thread.interrupted();
throw new InterruptedException();
}
}
公平模式
基于队列,先进先出,从尾部添加,从头部取
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
//是put操作(isData是true)还是take
boolean isData = (e != null);
for (;;) {
//对于当前线程来说,t是尾节点,h是头节点
QNode t = tail;
QNode h = head;
if (t == null || h == null) // 不用考虑,只要new一个实例,t和h都会指向一个虚拟节点
continue;
//队列是空,或(当前操作类型与队列的节点操作类型相同)
//队列最初先执行的put那么插入的节点都是put节点(isData是true),
//反之最初执行的是take,那么插入的都是take节点即isData是false
if (h == t || t.isData == isData) { // 空或相同模式
QNode tn = t.next;
//t最初已赋值为tail,如果这时不相等,必定是有其它线程往队列中
//插入的新节点,那么当前操作需要重新定位头尾节点,重新执行for
if (t != tail) // inconsistent read
continue;
//tn是t(也就是tail尾节点)的下一个节点,正常情况下应该是null
//如果不是null说明其它线程往队列里插入了新节点,如果另一个线程
//尾部插入新节点是还没来得及更新tail的引用,那么当前线程“帮忙”更新下,
//并重新执行for重新定位头尾节点
if (tn != null) { // lagging tail
advanceTail(t, tn);
continue;
}
if (timed && nanos <= 0) // can't wait
return null;
//第一次总是null只有下面t.casNext(null, s)失败重新循环是才无需再次new
if (s == null)
s = new QNode(e, isData);
//如果当前位节点的next没有被其它线程更新,则设置尾节点的下个节点为s
if (!t.casNext(null, s)) // failed to link in
continue;
//将tail指针更新为s节点
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的else分支执行advanceHead(h, m);LockSupport.unpark(m.waiter);后唤起当前线程并进入此if分支
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
//互斥模式:
//即队列中的节点isData是true(节点类型是put),那么当前必定是take操作
//相反队列中的节点isDate是false(节点类型是take),那么当前必定是put操作
QNode m = h.next; // node to fulfill
//公平模式下遵循先进先出原则,如果此时t!=tail或m==null或h!=head
//说明另一个线程执行了插入或读取(删除)操作,那么这里自旋以便重新获取头尾节点
if (t != tail || m == null || h != head)
continue; // inconsistent read
Object x = m.item;
//另外一个线程已经抢先执行了相同操作,如当前是put(isData是true)
//另外一个线程抢先执行put此时x!=null是true,那么执行advanceHead删除一个节点(头节点后移一位)
if (isData == (x != null) || // m already fulfilled
//x与m相等说明执行了删除操作,即线程执行了interrupt
x == m || // m cancelled
//没有设置成功,说明其它线程抢先“取走”了数据,同样的移动头结点
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
//未成功对节点进行出队,再次循环尝试
continue;
}
//成功交换数据,移动头节点,并唤醒阻塞的线程,使其继续进行后续操作
//交换分2种情况:
//1. 上个线程w在take上因没有数据而阻塞,当前线程put数据后,w唤醒,w线程成功取到数据,得以继续
//2.上个线程在w上执行put操作,因为数据已有而使得put操作阻塞,当前线程执行take后将数据取走,并唤醒w,w线程恢复得以继续put
advanceHead(h, m); // successfully fulfilled
LockSupport.unpark(m.waiter);
//当前线程take操作返回节点的item的值x,如果是put操作返回put的值本身e
return (x != null) ? (E)x : e;
}
}
}
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();
//如果当前队列只有这唯一的一个node那么需要进行自旋,自旋的目的是为了提高性能
//,因为可能存在一个或多个消费者线程急需取数据,取操作在自旋时就已经完成,
//有效避免了一次对当前线程挂起与恢复的开销,如果队列中有多个节点那无需
//自旋,直接挂起当前线程
int spins = ((head.next == s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
//当前线程是否调用过w.interrupt()唤醒操作
if (w.isInterrupted())
//将当前节点s的item指向s即s.item=s
s.tryCancel(e);
Object x = s.item;
//3种情况会进入if直接返回x
//1.被挂起的线程执行了interrupt方法
//2.线程w因put挂起,另一个线程取走了与线程w关联的节点的数据(take操作)
//3.线程w因take挂起,另一个线程往与线程w关联的节点进行了put操作
if (x != e)
return x;
if (timed) {
nanos = deadline - System.nanoTime();
//指定了超时时间,其put的值没有被消费,或没生产者导致take仍获取不到值
//其实就不满足x!=e,那么执行当前节点的tryCancel方法
if (nanos <= 0L) {
//执行s.item=s以便再次自旋时能够满足if (x != e)并退出当前方法
//最终执行clean方法删除节点
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);
}
}
put方法
//put方法属于阻塞操作,除非成功进行了put或调用了当前线程的interrupt方法
//否则不会有返回
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
//执行put操作或take操作都调用同一个方法transfer,对于put调用transfer成功执行返回值是
//要放入的元素e,对于take调用transfer返回值是另外一个线程put进去的值,因此正常情况下transfer返回值是非空的,只有对当前线程执行interrupt进行终止时才会返回null
if (transferer.transfer(e, false, 0) == null) {
//清除状态标识并手动抛异常,这也是为什么在InterruptedException的catch块中isInerrupted总返回false
Thread.interrupted();
throw new InterruptedException();
}
}
clean方法
//pred是s的前一个节点,即pred.next等于s
void clean(QNode pred, QNode s) {
s.waiter = null; // forget thread
while (pred.next == s) { // Return early if already unlinked
QNode h = head;
//第一个节点
QNode hn = h.next; // Absorb cancelled first node as head
//如果第一个节点已经删除即hn.item等于hn,那么移动head并重新自旋
if (hn != null && hn.isCancelled()) {
//需要注意的是频繁删除尾节点可能会导致节点链中存在部分item自引用的节点
//这里也可以帮忙进一步清除
advanceHead(h, hn);
continue;
}
QNode t = tail; // Ensure consistent read for tail
if (t == h)//队列是空直接返回
return;
QNode tn = t.next;
//t不等于tail说明另一个线程修改了尾节点,重新自旋,再次定位头尾
if (t != tail)
continue;
//tn不等于null说明另一个线程在尾节点插入了新节点,如果尾节点指针还没更新,则帮忙更新下,继续自旋,重新定位头尾
if (tn != null) {
advanceTail(t, tn);
continue;
}
//最好的情况下,删除的节点不是尾节点,只需将pre节点的next更新为要删除节点s的next
if (s != t) { // 非尾节点
QNode sn = s.next;
//sn等于s说明已经删除,否则更新并返回
if (sn == s || pred.casNext(s, sn))
return;
}
//如果清除的是尾节点:
//cleanMe保持上次要清除的节点的pre
//如果发现cleanMe是null,则将清除节点的pre赋值给它,然后什么都不做,等下次清理尾节点时发现cleanMe有值再执行清理操作
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
//清理完成后将cleanMe设为null
casCleanMe(dp, null);
//相等说明当前pred已经保存到cleanMe,下次再清理,直接返回
if (dp == pred)
return; // s is already saved node
} else if (casCleanMe(null, pred))
//先保存要清理节点的前驱,以便下次执行清理,让后直接返回
//为什么尾节点不立即清除,因为清理尾节点需要更新tail指针到前一个节点
//而当前节点并没有前驱节点的引用,因此通过这种方式规避
return; // Postpone cleaning s
}
}
非公平模式
基于栈,后进先出,从头部添加,再从头部取
E transfer(E e, boolean timed, long nanos) {
//当前要插入的节点
SNode s = null; // constructed/reused as needed
int mode = (e == null) ? REQUEST : DATA;//take还是put
for (;;) {
SNode h = head;//第一次head是null
//队列是空或模式相同为同模式,同模式会进行入队操作
if (h == null || h.mode == mode) { // empty or same-mode
//已经超时直接返回,顺便看看需不需要更新head
if (timed && nanos <= 0) { // can't wait
if (h != null && h.isCancelled())
casHead(h, h.next); // pop cancelled node
else
return null;
//创建一个新节点s(为null创建)s的netx是h,并将新节点设为head
//如果设置失败,再次自旋重试
} else if (casHead(h, s = snode(s, e, h, mode))) {
//将节点插入到开头后尝试挂起线程
SNode m = awaitFulfill(s, timed, nanos);
//线程恢复后看返回值m和s是否是同一个节点,如果是,说明
//节点s被取消了,要么执行了线程的interrupt要么操作超时
if (m == s) { // wait was cancelled
clean(s);//尝试清除取消的节点
return null;
}
//此if成立的条件是:另一个线程在当前的else if分支中进行了“取操作”
//导致当前节点s已经和s之前的某个fulfilling节点进行了匹配,但还没
//来得及将s和匹配的fulfilling节点弹出,那么这里帮助另一个线程执行弹出
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
//相异模式,且只有当前线程在取(即头结点不是fulfilling模式)
//当前头节点已经删除,帮助其它线程将头结点后移(线程间的互助)
if (h.isCancelled()) // already cancelled
casHead(h, h.next); // pop and retry
//在开头插入一个FULFILLING模式的节点s(站位),head指向s
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
//已全部弹出,栈是空将head设为null
if (m == null) { // all waiters are gone
casHead(s, null); // pop fulfill node
//继续自旋重新并重新new一个s节点
s = null; // use new node next time
break; // restart main loop
}
SNode mn = m.next;
//将当前节点m与前一个fulfilling节点s相匹配,并恢复节点m的线程
if (m.tryMatch(s)) {
//成功匹配弹出(m和s),将头节点指向后面的节点mn
casHead(s, mn); // pop both s and m
//匹配成返回相关值take取m.item,put返回放入的值
return (E) ((mode == REQUEST) ? m.item : s.item);
} else // lost match
//匹配失败说明另一行线程已经匹配,如果另一个线程还没来得及
//执行弹出,这里帮忙弹一下,并将s(fulfilling)的next指向mn
s.casNext(m, mn); // help unlink
}
}
} else { // help a fulfiller
//相异模式
//头节点是fulfilling模式,说明有节点正在匹配(正在取操作)
//那么执行助推操作后自旋完成当前操作流程
SNode m = h.next; // m is h's match
//已全部弹出,栈是空将head设为null
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
}
}
}
}
boolean tryMatch(SNode s) {
//当前节点的match属性是空则将match属性设置为s(s为fulfilling类型节点)
if (match == null &&
UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
Thread w = waiter;
//准备恢复当前相关(put/take)操作的线程
if (w != null) { // waiters need at most one unpark
waiter = null;
LockSupport.unpark(w);
}
return true;
}
return match == s;
}
static SNode snode(SNode s, Object e, SNode next, int mode) {
if (s == null) s = new SNode(e);
s.mode = mode;
s.next = next;
return s;
}
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
//如果当前队列是空或者s就是头节点或者头结点是isFulfilling模式(有线程正在取数据),那么可以自旋一段时间,因为在自旋的周期内
//其他线程可以取走数据,避免了一次无意义的线程阻塞唤醒操作
int spins = (shouldSpin(s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
if (w.isInterrupted())
//如果当前线程被打断则取消节点(属性match赋值为this自己;)
//紧接着下面的if会成功执行退出,最后执行clean(s);
s.tryCancel();
SNode m = s.match;
if (m != null)
//match不为空,要么s被取消,要么s和前面的fulfilling节点匹配(其它线程进行了相反操作,如take)
return m;
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
//操作超时则取消节点
//如put操作,10秒后还没有进行take,将队列中的当前节点取消
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);
}
}
/**
*清除节点
*/
void clean(SNode s) {
//清除属性
s.item = null; // forget item
s.waiter = null; // forget thread
SNode past = s.next;
if (past != null && past.isCancelled())
//如果当前节点s的下个节点已经取消则跳过取消的节点
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
//到这里p指向第一个非取消的节点
while (p != null && p != past) {
//这个循环不仅仅只是清理s,而是清理栈上所有已取消的节点
SNode n = p.next;
if (n != null && n.isCancelled())
//如果p的下个节点已经取消,那么更新p的next指针
p.casNext(n, n.next);
else
p = n;
}
}