一、說(shuō)明:SynchronousQueue是堵塞隊(duì)列得一種,其特性是put操作和take操作必須是互相喚醒的,比如:put操作,必須take操作喚醒,take操作必須put操作喚醒,沒(méi)有take操作,多個(gè)put操作都堵塞,等待一個(gè)一個(gè)take操作來(lái)一個(gè)一個(gè)喚醒,同理,take操作也一樣,跟他的名字一樣,同步隊(duì)列,就好像,你是救我的藥,我也是救你的藥。
誤區(qū):很多人把其認(rèn)為其沒(méi)有容量,不存儲(chǔ)元素,這是錯(cuò)的。
正解: SynchronousQueue的公平隊(duì)列TransferQueue是一個(gè)單項(xiàng)鏈表,是會(huì)存儲(chǔ)元素的。
二、源碼分析
從構(gòu)造方法入手??创a注釋。
無(wú)參構(gòu)造方法,默認(rèn)給得參數(shù)是false,也就是非公平隊(duì)列。
非公平隊(duì)列使用得是TransferStack, 公平隊(duì)列使用得是TransferQueue。公平和非公平也就是put和take數(shù)據(jù)的順序是否是公平的,也就是先放則先拿,后放則后拿。
/**
* Creates a {@code SynchronousQueue} with nonfair access policy.
*/
// 無(wú)參構(gòu)造方法,默認(rèn)給得參數(shù)是false,也就是非公平隊(duì)列。
public SynchronousQueue() {
this(false);
}
/**
* Creates a {@code SynchronousQueue} with the specified fairness policy.
*
* @param fair if true, waiting threads contend in FIFO order for
* access; otherwise the order is unspecified.
*/
// 非公平隊(duì)列使用得是TransferStack, 公平隊(duì)列使用得是TransferQueue。
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
這里我們研究公平隊(duì)列,也就是傳參true。
TransferQueue類(lèi)中有一個(gè)靜態(tài)內(nèi)部類(lèi)Qnode,TransferQueue中存儲(chǔ)得一個(gè)元素就是一個(gè)Qnode,Qnode有next,說(shuō)明是一個(gè)單項(xiàng)鏈表
static final class TransferQueue<E> extends Transferer<E> {
/*
* This extends Scherer-Scott dual queue algorithm, differing,
* among other ways, by using modes within nodes rather than
* marked pointers. The algorithm is a little simpler than
* that for stacks because fulfillers do not need explicit
* nodes, and matching is done by CAS'ing QNode.item field
* from non-null to null (for put) or vice versa (for take).
*/
/** 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;
}
TransferQueue中還有兩個(gè)變量。1、head,頭節(jié)點(diǎn)。2、tail,尾節(jié)點(diǎn)。
/** Head of queue */
transient volatile QNode head;
/** Tail of queue */
transient volatile QNode tail;
/**
* Reference to a cancelled node that might not yet have been
* unlinked from queue because it was the last inserted node
* when it was cancelled.
*/
transient volatile QNode cleanMe;
還有三個(gè)方法。方法作用看代碼注釋。
advanceHead:cas替換,把TransferQuene的舊頭節(jié)點(diǎn)替換為新節(jié)點(diǎn),舊頭節(jié)點(diǎn)得next指向舊頭節(jié)點(diǎn)自己。
advanceTail:cas替換,把TransferQuene的舊尾節(jié)點(diǎn)替換為新節(jié)點(diǎn)。
/**
* Tries to cas nh as new head; if successful, unlink
* old head's next node to avoid garbage retention.
*/
// cas替換,把TransferQuene的舊頭節(jié)點(diǎn)替換為新節(jié)點(diǎn),舊頭節(jié)點(diǎn)得next指向舊頭節(jié)點(diǎn)自己。
void advanceHead(QNode h, QNode nh) {
if (h == head &&
UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
h.next = h; // forget old next
}
/**
* Tries to cas nt as new tail.
*/
// cas替換,把TransferQuene的舊尾節(jié)點(diǎn)替換為新節(jié)點(diǎn)。
void advanceTail(QNode t, QNode nt) {
if (tail == t)
UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
}
/**
* Tries to CAS cleanMe slot.
*/
// 同上
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp &&
UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
}
接下來(lái)跟著SynchronousQueue的put方法走看源碼。
調(diào)用構(gòu)造方法的時(shí)候,就創(chuàng)建了一個(gè)dummy node(假節(jié)點(diǎn)),TransferQueue的頭和尾指針都指向這個(gè)節(jié)點(diǎn)。也就是這種結(jié)構(gòu):head(tail)(dummy)
// 測(cè)試方法
@Test
public void testPut() throws InterruptedException {
SynchronousQueue<String> queue = new SynchronousQueue<>(true);
queue.put("hello");
}
// SynchronousQueue的構(gòu)造方法,我們傳入了true,所以使用的queue是TransferQueue。
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
// 調(diào)用構(gòu)造方法的時(shí)候,就創(chuàng)建了一個(gè)dummy node(假節(jié)點(diǎn)),TransferQueue的頭和尾指針都指向這個(gè)節(jié)點(diǎn)。
TransferQueue() {
QNode h = new QNode(null, false); // initialize to dummy node.
head = h;
tail = h;
}
static final class QNode {
// next 指針
volatile QNode next; // next node in queue
// 實(shí)際保存的元素
volatile Object item; // CAS'ed to or from null
// 鎖和喚醒的線程
volatile Thread waiter; // to control park/unpark
// 是否是數(shù)據(jù)(put則為true,take則會(huì)false)
final boolean isData;
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
然后看起put方法
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();
}
}
// e:put的元素。timed:是否具有超時(shí)機(jī)制。nanos:超時(shí)時(shí)間。
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
// put操作這里e不為null,isData為true
boolean isData = (e != null);
// 自旋
for (;;) {
QNode t = tail;
QNode h = head;
// 還沒(méi)有初始化,則continue重新來(lái)過(guò)
if (t == null || h == null) // saw uninitialized value
continue; // spin
// h == t則說(shuō)明還沒(méi)有存入元素。這是為true,進(jìn)入。進(jìn)入這個(gè)if就會(huì)堵塞元素,不管take還是put。
if (h == t || t.isData == isData) { // empty or same-mode
// tn為null。(順便說(shuō)一句:next變量是volatile修飾,多線程可見(jiàn),所以下面這個(gè)復(fù)制操作是線程安全的。)
QNode tn = t.next;
// 其他線程把尾節(jié)點(diǎn)改變了,則再旋一次
if (t != tail) // inconsistent read
continue;
// tn為null
if (tn != null) { // lagging tail
advanceTail(t, tn);
continue;
}
// 無(wú)超時(shí)機(jī)制
if (timed && nanos <= 0) // can't wait
return null;
// 創(chuàng)建put操作存放數(shù)據(jù)的新節(jié)點(diǎn),isData為true。
// 如果沒(méi)有元素,take操作進(jìn)入這里,也創(chuàng)建了一個(gè)新node,也就是說(shuō)take操作堵塞的時(shí)候,也會(huì)創(chuàng)建節(jié)點(diǎn),
if (s == null)
s = new QNode(e, isData);
// cas替換,從:head(tail)(dummy)到head(tail)(dumy)->S
// cas替換,尾節(jié)點(diǎn)的next指向新建立的s節(jié)點(diǎn)。失敗則再旋一次
if (!t.casNext(null, s)) // failed to link in
continue;
/*cas替換,把TransferQuene的舊尾節(jié)點(diǎn)t替換為新節(jié)點(diǎn)s。
至此,變成了head(dumy)=>s(tail)
*/
advanceTail(t, s); // swing tail and wait
// 下面這個(gè)方法調(diào)用就是具體的堵塞調(diào)用,看下一段代碼分析
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
*/
// s是新節(jié)點(diǎn),e是put的真正數(shù)據(jù),timed是是否超時(shí),nanos是超時(shí)時(shí)間
Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
/* Same idea as TransferStack.awaitFulfill */
// deadLine為0
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
// 自旋次數(shù),第一次put的時(shí)候,鏈表結(jié)構(gòu)為:head(dumy)=>s(tail),所以
// head.hext == s,timed為不超時(shí)false,所以spin=maxUntimedSpins=512,
// 如果第二個(gè)put堵塞,則結(jié)構(gòu)為:head(dumy)=>s=>s1(tail),而head.next和s1(新put的元素是s1)不相等,所以,spin=0直接堵塞。
//(為什么會(huì)自旋,我估計(jì)是為了,高并發(fā)下,take操作
// 和put操作很可能再極短時(shí)間進(jìn)行,這樣的話(huà),就不需要喚醒線程和堵塞線程)
int spins = ((head.next == s) ?
(timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
// 當(dāng)前線程準(zhǔn)備中斷,則s節(jié)點(diǎn)的item指向s節(jié)點(diǎn)自己
if (w.isInterrupted())
s.tryCancel(e);
Object x = s.item;
// 這里為false,只有當(dāng)其他線程把這個(gè)元素替換了,比如put堵塞在這
// 里的時(shí)候,take就會(huì)把這個(gè)元素替換掉,然后put喚醒的時(shí)候就能直接return了。
if (x != e)
return x;
if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
s.tryCancel(e);
continue;
}
}
// 這里spins初始為512,一直遞減到0
if (spins > 0)
--spins;
else if (s.waiter == null)
// node節(jié)點(diǎn)和waiter和當(dāng)前線程關(guān)聯(lián)上,為了公平的喚醒。
s.waiter = w;
else if (!timed)
// 鎖住當(dāng)前線程。設(shè)置thread的block變量為parkBlocker指向的對(duì)象transferQueue。
LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
}
再來(lái)看take方法
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
// isData為false
boolean isData = (e != null);
for (;;) {
QNode t = tail;
QNode h = head;
if (t == null || h == null) // saw uninitialized value
continue; // spin
// 第一put后結(jié)構(gòu)變成了:head(dumy)=>s(tail)
/* 所以這里h不等于t,t.isData為true,
*所以這里不成立,走else塊。只要不堵塞,都會(huì)走else塊。put操作如
果不堵塞,也會(huì)走else塊。
*/
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
//head(dumy)=>s(tail)
// m就是之前put的哪個(gè)節(jié)點(diǎn)
QNode m = h.next; // node to fulfill
if (t != tail || m == null || h != head)
continue; // inconsistent read
Object x = m.item;
/*
isData=(e!=null),這是take操作,所以e為null,所以isData為false,x為之前put進(jìn)去的值,為非null
x == m說(shuō)明已經(jīng)取消了,之前put操作的時(shí)候,
*awaitFulfill方法里,如果當(dāng)前線程準(zhǔn)備中斷,
*就會(huì)調(diào)用qnode的tryCancel方法,讓qnode的next指向自己,代表
* 這個(gè)節(jié)點(diǎn)取消了。
head(dumy)=>s(tail)
*!m.casItem(x, e):直接替換item的值,
這樣,在take方法堵塞在awaitFulfill方法里的時(shí)候,
這里直接把之前take方法創(chuàng)建的node的item改掉,
然后喚醒take的線程,然后take操作獲取到這個(gè)新值了和它之前的值不一樣,則直接跳出循環(huán),不堵塞。
*/
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
advanceHead(h, m); // dequeue and retry
continue;
}
// 變成了head(s)(tail)
advanceHead(h, m); // successfully fulfilled
LockSupport.unpark(m.waiter);
return (x != null) ? (E)x : e;
}
}
}
SynchronousQueue還有offer方法,poll方法。add方法。
offer方法,不會(huì)堵塞,可以存進(jìn)去,則存進(jìn)去(poll或take操作正在堵塞等著獲取元素),否則,直接返回false。
poll方法可以有超時(shí)時(shí)間,和take差不多,take沒(méi)有超時(shí)時(shí)間。
add方法,調(diào)用的就是offer方法,不過(guò)add方法,添加不進(jìn)去,則直接報(bào)錯(cuò)。
總結(jié)
新建TransQueue初始化結(jié)構(gòu)為:head(dumy)(tail)
第一個(gè)次put后:head(dumy)=>s(tail)
第二個(gè)次put后:head(dumy)=>s=>s1(tail)
第一次put后緊接著take后:head(s)(tail)
鏈表中沒(méi)有put過(guò)的節(jié)點(diǎn),則自旋512次,有,則直接堵塞
put和take都會(huì)存儲(chǔ)元素,take存儲(chǔ)的元素item為null,再堵塞之后,put操作會(huì)cas替換這個(gè)元素的item,然后喚醒take操作,獲取這個(gè)新元素。