上一篇文章剖析了 LinkedBlockingQueue 的相關(guān)源碼,那這篇文章接著看另外一個(gè)常見的阻塞隊(duì)列 —— SynchronousQueue
簡(jiǎn)介
SynchronousQueue 是一個(gè)比較特殊的阻塞隊(duì)列類,為什么這樣說呢?我們不妨從官方的類注釋說起...
根據(jù)類注釋可大概得出以下幾點(diǎn):
- 每一個(gè)插入操作都必須等待另一個(gè)線程完成刪除操作
- 隊(duì)列沒有內(nèi)部容量,所以不能迭代數(shù)據(jù)
- 可以選擇公平策略。公平策略是使用
隊(duì)列
先入先出,非公平策略是使用堆棧
先入后出
咦?SynchronousQueue 對(duì)象沒有容量,那這個(gè)阻塞隊(duì)列的使用場(chǎng)景是什么呢?
其實(shí)線程池的其中一種實(shí)現(xiàn)——Executors.newCachedThreadPool
就使用了SynchronousQueue作為阻塞隊(duì)列
那先從一個(gè)demo開始揭開 SynchronousQueue 的廬山真面目吧!
public class SynchronousQueueDemo {
public static void main(String[] args) throws InterruptedException {
SynchronousQueue synchronousQueue = new SynchronousQueue();
new Thread(() -> {
try {
synchronousQueue.put("Hello World!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "線程一").start();
new Thread(() -> {
try {
System.out.println(synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "線程二").start();
}
}
示例中為什么在main方法里使用兩個(gè)線程分別執(zhí)行put和take操作呢?因?yàn)樵谕痪€程中,有可能存在先執(zhí)行take操作,當(dāng)程序執(zhí)行take方法的時(shí)候發(fā)現(xiàn)隊(duì)列為空就會(huì)阻塞當(dāng)前線程,那么之后的put方法就不會(huì)執(zhí)行,線程將會(huì)一直等待。
源碼剖析
成員變量
// SynchronousQueue 定義的抽象類,由 TransferStack 和 TransferQueue 實(shí)現(xiàn)
private transient volatile Transferer<E> transferer;
// CPU 數(shù)量
static final int NCPUS = Runtime.getRuntime().availableProcessors();
// 自旋次數(shù),如果transfer指定了timeout時(shí)間,則使用maxTimeSpins,如果CPU數(shù)量小于2則自旋次數(shù)為0,否則為32。不會(huì)隨CPU數(shù)量增加而變化
static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
// 自旋次數(shù),如果沒有指定時(shí)間設(shè)置,則使用maxUntimedSpins。如果NCPUS數(shù)量大于等于2則設(shè)定為為32*16,否則為0
static final int maxUntimedSpins = maxTimedSpins * 16;
// 為了防止自定義的時(shí)間限過長(zhǎng),為了優(yōu)化而設(shè)置,如果自定義時(shí)間長(zhǎng)于這個(gè)值則取默認(rèn)的 spinForTimeoutThreshold ,單位為納秒。
static final long spinForTimeoutThreshold = 1000L;
構(gòu)造函數(shù)
// 默認(rèn)使用非公平策略
public SynchronousQueue() {
this(false);
}
// fair為false是非公平策略,使用的數(shù)據(jù)結(jié)構(gòu)是棧;fair為true是公平策略,使用的數(shù)據(jù)結(jié)構(gòu)是隊(duì)列
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
先列出相關(guān)方法的源碼,但并沒有加上注釋,因?yàn)楹诵姆椒ǘ荚?Transferer 對(duì)象中聲明?。ㄖ档米⒁獾氖?,SynchronousQueue類并沒有實(shí)現(xiàn)remove、removeAll、peek、clear等方法,都是使用默認(rèn)值)
添加(add、offer、put)、刪除、查找元素
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
if (e == null) throw new NullPointerException();
if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
return true;
if (!Thread.interrupted())
return false;
throw new InterruptedException();
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
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();
}
}
public E poll() {
return transferer.transfer(null, true, 0);
}
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
相對(duì)于 ArrayBlockingQueue 和 LinkedBlockingQueue,可以發(fā)現(xiàn)類似poll、take等相關(guān)方法都被抽象成統(tǒng)一方法來進(jìn)行操作,通過抽象出內(nèi)部類 Transferer 實(shí)現(xiàn)不同的操作。接下來,咱們重點(diǎn)看看公平模式與非公平模式下的源碼。
1.SynchronousQueue 的非公平模式(TransferStack)
眾所周知,堆棧是FILO(First in last out)的方式,所以也可以理解為非公平模式為什么使用棧這種數(shù)據(jù)結(jié)構(gòu),如果排隊(duì)的時(shí)候第一個(gè)進(jìn)來,最后一個(gè)才能走,這很不公平嘛!
在 TransferStack 內(nèi)部有 REQUEST、DATA、FULFILLING
這三個(gè)狀態(tài)。
REQUEST 表示請(qǐng)求從棧獲取數(shù)據(jù)操作的消費(fèi)者,如:take 方法;
DATA 表示往棧內(nèi)部放數(shù)據(jù)的生產(chǎn)者,如:put 方法;
FULFILLING 表示正在交易的生產(chǎn)者或消費(fèi)者
REQUEST 和 DATA 這兩種狀態(tài)理解起來還不難,但或許 FULFILLING 還是不太清楚有什么用,先帶著疑問往下去看,現(xiàn)在只需要簡(jiǎn)單的理解為:不同狀態(tài) REQUEST 和 DATA 可以相互匹配的,當(dāng)與棧頂匹配后就會(huì)將他們狀態(tài)轉(zhuǎn)換為 FULFILLING,當(dāng)匹配成功后就會(huì)將棧頂和匹配的元素一同出棧。
成員變量
//表示一個(gè)未填充的消費(fèi)者
static final int REQUEST = 0;
//表示一個(gè)未填充的生產(chǎn)者
static final int DATA = 1;
// 表示生產(chǎn)者正在給等待資源的消費(fèi)者補(bǔ)給資源,或生產(chǎn)者在等待消費(fèi)者消費(fèi)資源
static final int FULFILLING = 2;
//棧的頭結(jié)點(diǎn)
volatile SNode head;
棧節(jié)點(diǎn)
// 棧節(jié)點(diǎn)
static final class SNode {
// 節(jié)點(diǎn)的后繼
volatile SNode next;
// 相匹配的節(jié)點(diǎn)
volatile SNode match;
// 等待的線程
volatile Thread waiter;
// item和mode不需要可見,由于他們總是在其他可見/原子操作寫之前,讀之后
Object item;// 數(shù)據(jù)
int mode;//節(jié)點(diǎn)模式
SNode(Object item) {
this.item = item;
}
// cas保證線程安全設(shè)置節(jié)點(diǎn)后繼節(jié)點(diǎn)
boolean casNext(SNode cmp, SNode val) {
return cmp == next && UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
//嘗試匹配目標(biāo)節(jié)點(diǎn)與本節(jié)點(diǎn),如果匹配,可以喚醒線程。補(bǔ)給者調(diào)用tryMatch方法,確定它們的等待線程。等待線程阻塞到它們自己被匹配。如果匹配返回true
boolean tryMatch(SNode s) {
// 設(shè)置本節(jié)點(diǎn)的匹配為s節(jié)點(diǎn)
if (match == null && UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
Thread w = waiter;
if (w != null) {
waiter = null;
LockSupport.unpark(w);
}
return true;
}
return match == s;
}
// 節(jié)點(diǎn)嘗試取消等待,match 從原來的 null 變?yōu)閠his
void tryCancel() {
UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
}
// match 指向自己,則取消等待
boolean isCancelled() {
return match == this;
}
}
核心方法
- isFulfilling:判斷指定類型是否是互補(bǔ)模式
- casHead(SNode h, SNode nh):替換當(dāng)前頭結(jié)點(diǎn)
- SNode snode(SNode s, Object e, SNode next, int mode):生成SNode節(jié)點(diǎn)對(duì)象
- transfer(E e, boolean timed, long nanos): 主要處理邏輯
- awaitFulfill(SNode s, boolean timed, long nanos): 等待fulfill操作
- shouldSpin(SNode s):判斷節(jié)點(diǎn)s是頭結(jié)點(diǎn)或是fulfill節(jié)點(diǎn)則返回true
- clean(SNode s):將head節(jié)點(diǎn)到S節(jié)點(diǎn)之間所有已經(jīng)取消的節(jié)點(diǎn)全部移出
// 如果m是一個(gè)填充為單元,則返回true
static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }
// 比較head是否為h,并且CAS操作nh為當(dāng)前head
boolean casHead(SNode h, SNode nh) {
return h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
}
//創(chuàng)建或重新設(shè)置節(jié)點(diǎn)的變量。在節(jié)點(diǎn)入棧時(shí)創(chuàng)建,在當(dāng)可能需要保證減少intervals(間隔)讀和head的CAS操或避免由于競(jìng)爭(zhēng)CAS操作節(jié)點(diǎn)入棧引起的垃圾時(shí),此方法會(huì)被transfer調(diào)用
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;
}
E transfer(E e, boolean timed, long nanos) {
// 1.如果隊(duì)列為空或已經(jīng)包含相同模式的節(jié)點(diǎn),則嘗試節(jié)點(diǎn)入棧,等待匹配返回,如果取消返回null。
// 2.如果包含一個(gè)互補(bǔ)模式的節(jié)點(diǎn)(take(REQUEST)->put(DATA);put(DATA)->take(REQUEST)),則嘗試一個(gè)FULFILLING節(jié)點(diǎn)入棧,同時(shí)匹配等待的協(xié)同節(jié)點(diǎn),兩個(gè)節(jié)點(diǎn)同時(shí)出棧,返回匹配的元素。由于其他線程執(zhí)行步驟3,實(shí)際匹配和解除鏈接指針動(dòng)作不會(huì)發(fā)生。
// 3.如果棧頂存在另外一個(gè)FULFILLING的節(jié)點(diǎn),則匹配節(jié)點(diǎn),并出棧。這段的代碼與fulfilling相同,除非沒有元素返回
SNode s = null;
// 根據(jù)元素判斷節(jié)點(diǎn)模式,元素不為null,則為DATA,否則為REQUEST
int mode = (e == null) ? REQUEST : DATA;
for (;;) {
//剛開始頭節(jié)點(diǎn)為null,第一個(gè)進(jìn)來的節(jié)點(diǎn)就是頭節(jié)點(diǎn)。
SNode h = head;
if (h == null || h.mode == mode) {// 如果是空隊(duì)列,或棧頭節(jié)點(diǎn)的模式與要放入的節(jié)點(diǎn)模式相同
if (timed && nanos <= 0) {
//如果超時(shí),則取消等待,出棧,設(shè)置棧頭為其后繼
if (h != null && h.isCancelled())
casHead(h, h.next);
else
return null;
} else if (casHead(h, s = snode(s, e, h, mode))) {
//如果非超時(shí),則將創(chuàng)建的新節(jié)點(diǎn)入棧成功,即放在棧頭,自旋等待匹配節(jié)點(diǎn)(timed決定是否超時(shí))
SNode m = awaitFulfill(s, timed, nanos);
// 返回的m == s 表示該節(jié)點(diǎn)被取消了或者超時(shí)、中斷了
if (m == s) {
// 如果返回的是自己,節(jié)點(diǎn)取消等待,從棧中移除,并遍歷棧移除取消等待的節(jié)點(diǎn)
clean(s);
return null;
}
if ((h = head) != null && h.next == s)
//s節(jié)點(diǎn)匹配成功,則設(shè)置棧頭為s的后繼
casHead(h, s.next);
// 匹配成功,REQUEST模式返回,匹配到的節(jié)點(diǎn)元素(DATA),DATA模式返回當(dāng)前節(jié)點(diǎn)元素
return (E) ((mode == REQUEST) ? m.item : s.item);
}
} else if (!isFulfilling(h.mode)) { // 如果棧頭節(jié)點(diǎn)模式不為Fulfilling,判斷是否取消等待,是則出棧
if (h.isCancelled()) // already cancelled
casHead(h, h.next); // pop and retry
else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { //非取消等待,則是節(jié)點(diǎn)入棧
for (;;) { // 自旋直到節(jié)點(diǎn)匹配或者等待節(jié)點(diǎn)都沒有
SNode m = s.next;
//后繼節(jié)點(diǎn)為null,則出棧
if (m == null) { // 堆棧中沒有等待節(jié)點(diǎn)
casHead(s, null); // 將棧頭節(jié)點(diǎn)位置設(shè)置為null
s = null; // 棧頭節(jié)點(diǎn)設(shè)置為null,便于GC
break; // 跳出當(dāng)前循環(huán),重新執(zhí)行主循環(huán)
}
SNode mn = m.next;
// 嘗試匹配 s 節(jié)點(diǎn)
if (m.tryMatch(s)) {
//匹配成功兩個(gè)節(jié)點(diǎn)則出棧
casHead(s, mn); // pop both s and m
return (E) ((mode == REQUEST) ? m.item : s.item);
} else
// 如果沒有匹配成功,則說明已經(jīng)有其它線程與 m 節(jié)點(diǎn)匹配了,將 mn 作為 s 的后繼節(jié)點(diǎn)
s.casNext(m, mn); // help unlink
}
}
} else {
//如果棧頭節(jié)點(diǎn)模式為Fulfilling,則代表?xiàng)n^節(jié)點(diǎn)正在與其它節(jié)點(diǎn)匹配,可以理解為協(xié)助棧頭節(jié)點(diǎn)匹配成功
SNode m = h.next; // m is h's match
if (m == null)
//如果無后繼節(jié)點(diǎn),則棧頭出棧
casHead(h, null); // pop fulfilling node
else {
//嘗試匹配,如果匹配成功,棧頭和匹配節(jié)點(diǎn)出棧,否則跳過后繼節(jié)點(diǎn)
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
}
}
}
}
// 自旋或阻塞,直到節(jié)點(diǎn)被一個(gè)fulfill操作匹配
SNode awaitFulfill(SNode s, boolean timed, long nanos) {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
//獲取自旋的次數(shù)
int spins = (shouldSpin(s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0);
for (;;) {
// 如果線程被中斷,則取消等待
if (w.isInterrupted())
s.tryCancel();
SNode m = s.match;
// 如果節(jié)點(diǎn)的匹配節(jié)點(diǎn)不為null,則返回匹配節(jié)點(diǎn)
if (m != null)
return m;
if (timed) {
nanos = deadline - System.nanoTime();
//如果超時(shí),則取消等待
if (nanos <= 0L) {
s.tryCancel();
continue;
}
}
// 如果自旋次數(shù)大于零,且可以自旋,則自旋次數(shù)減1
if (spins > 0)
spins = shouldSpin(s) ? (spins-1) : 0;
else if (s.waiter == null)
//如果節(jié)點(diǎn)S的等待線程為空,則設(shè)置當(dāng)前節(jié)點(diǎn)為S節(jié)點(diǎn)的等待線程,以便可以park后繼節(jié)點(diǎn)。
s.waiter = w; // establish waiter so can park next iter
else if (!timed)
//非超時(shí)等在者,park當(dāng)前線程
LockSupport.park(this);
else if (nanos > spinForTimeoutThreshold)
//如果超時(shí)時(shí)間大于,最大自旋閾值,則超時(shí)park當(dāng)前線程
LockSupport.parkNanos(this, nanos);
}
}
// 如果節(jié)點(diǎn)在棧頭或棧頭為FULFILLING的節(jié)點(diǎn),則返回true
boolean shouldSpin(SNode s) {
//因?yàn)楹芸赡芰⒖叹蜁?huì)有新的線程到來,那么就會(huì)立刻進(jìn)行交易而不需要進(jìn)行阻塞,然后被喚醒,這是需要過程的,所以這樣的自旋等待是值得的。
SNode h = head;
return (h == s || h == null || isFulfilling(h.mode));
}
// 將head節(jié)點(diǎn)到S節(jié)點(diǎn)之間所有已經(jīng)取消的節(jié)點(diǎn)全部移出。
void clean(SNode s) {
s.item = null; // forget item
s.waiter = null; // forget thread
SNode past = s.next;
if (past != null && past.isCancelled())
past = past.next;
// 如果取消的是頭節(jié)點(diǎn)則運(yùn)行下面的清理操作,操作邏輯很簡(jiǎn)單就是判斷頭結(jié)點(diǎn)是不是取消節(jié)點(diǎn),如果是則將節(jié)點(diǎn)一定到下一個(gè)節(jié)點(diǎn)
SNode p;
while ((p = head) != null && p != past && p.isCancelled())
//p是從頭節(jié)點(diǎn)開始第一個(gè)不移除的節(jié)點(diǎn)
casHead(p, p.next);
// 取消不是頭結(jié)點(diǎn)的嵌套節(jié)點(diǎn)
while (p != null && p != past) {
SNode n = p.next;
if (n != null && n.isCancelled())
//移除節(jié)點(diǎn)n
p.casNext(n, n.next);
else
p = n;
}
}
因?yàn)槎褩5某鰲:腿霔2僮鞫荚?transfer 方法里面,所以不容易理解。建議讀者多看幾遍,結(jié)合下面的圖和我個(gè)人分析的思路,一步一步的debug,這樣理解起來應(yīng)該就不難啦~
1.根據(jù)節(jié)點(diǎn)模式判斷是入棧(put)還是出棧(take)操作
2.判斷棧頭是否為空或棧頭節(jié)點(diǎn)操作是否和本次一樣,是的話執(zhí)行第3步,否則執(zhí)行第6步
3.判斷是否是超時(shí)操作,如果是超時(shí)操作的話則執(zhí)行第4步,否則執(zhí)行第5步
4.判斷棧頭是否非空并且是否可以取消,是的話將棧頭后繼節(jié)點(diǎn)cas操作成為棧頭節(jié)點(diǎn)后執(zhí)行第1步,否則返回null
5.cas操作創(chuàng)建節(jié)點(diǎn)并將該節(jié)點(diǎn)入棧,自旋等待匹配節(jié)點(diǎn)
6.判斷棧頭節(jié)點(diǎn)模式是否為Fulfilling,如果不是的話執(zhí)行第7步,否則執(zhí)行第10步
7.判斷棧頭節(jié)點(diǎn)是否需要取消等待,需要取消等待的話將棧頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)cas操作成為頭節(jié)點(diǎn)后重新執(zhí)行第1步,否則執(zhí)行第8步
8.棧頭節(jié)點(diǎn)不需取消等待,將當(dāng)前(take or put or poll)操作封裝為一個(gè)節(jié)點(diǎn)入棧后自旋堆棧,直到棧頭節(jié)點(diǎn)與棧中其它節(jié)點(diǎn)匹配后兩個(gè)節(jié)點(diǎn)都出棧返回節(jié)點(diǎn)信息或者所有的等待節(jié)點(diǎn)都沒有后跳出子循環(huán)重新執(zhí)行第1步
9.棧頭節(jié)點(diǎn)模式為Fulfilling,如果棧頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)為null,cas設(shè)置棧頭節(jié)點(diǎn)為null并執(zhí)行第1步,否則繼續(xù)嘗試匹配棧中其它節(jié)點(diǎn)
2.SynchronousQueue 的公平模式(TransferQueue)
公平模式下使用的數(shù)據(jù)結(jié)構(gòu)是隊(duì)列,其方式是先進(jìn)先出(FIFO:First In First Out)。就比如說咱們?cè)诮Y(jié)賬排隊(duì)的時(shí)候,肯定是先排隊(duì)的人先結(jié)賬呀,這樣才公平!
隊(duì)列節(jié)點(diǎn)
// 隊(duì)列節(jié)點(diǎn)
static final class QNode {
// 下一個(gè)節(jié)點(diǎn)
volatile QNode next;
// 元素信息
volatile Object item;
// 當(dāng)前等待的線程
volatile Thread waiter;
// 是否是數(shù)據(jù)(put的時(shí)候是true,take的時(shí)候是false)
final boolean isData;
QNode(Object item, boolean isData) {
this.item = item;
this.isData = isData;
}
// 替換當(dāng)前節(jié)點(diǎn)的next節(jié)點(diǎn)
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// 替換當(dāng)前節(jié)點(diǎn)的item數(shù)據(jù)
boolean casItem(Object cmp, Object val) {
return item == cmp &&
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
// 取消當(dāng)前操作,將當(dāng)前item賦值為this(當(dāng)前QNode節(jié)點(diǎn))
void tryCancel(Object cmp) {
UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
}
// 如果item是this(當(dāng)前QNode節(jié)點(diǎn))的話就返回true,反之返回false
boolean isCancelled() {
return item == this;
}
// 如果已知此節(jié)點(diǎn)離隊(duì)列,判斷next節(jié)點(diǎn)是不是為this,則返回true
boolean isOffList() {
return next == this;
}
}
成員變量
// 隊(duì)列頭節(jié)點(diǎn)
transient volatile QNode head;
// 隊(duì)列尾節(jié)點(diǎn)
transient volatile QNode tail;
// 節(jié)點(diǎn)被取消但沒有從隊(duì)列中移除
transient volatile QNode cleanMe;
核心方法
- advanceHead(QNode h, QNode nh):更新頭節(jié)點(diǎn)
- advanceTail(QNode t, QNode nt):更新尾節(jié)點(diǎn)
- casCleanMe(QNode cmp, QNode val):更新 cleanMe 節(jié)點(diǎn)
- awaitFulfill(QNode s, E e, boolean timed, long nanos):等待fulfill操作
- clean(QNode pred, QNode s):清空cleanMe節(jié)點(diǎn)
- transfer(E e, boolean timed, long nanos): 主要處理邏輯
void advanceHead(QNode h, QNode nh) {
if (h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
h.next = h; // forget old next
}
void advanceTail(QNode t, QNode nt) {
if (tail == t)
UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
}
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp && UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
}
Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
// 和 TransferStack.awaitFulfill 方法的邏輯一樣,因此就不顯示方法的邏輯啦
}
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
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;
// 判斷現(xiàn)在的t是不是末尾節(jié)點(diǎn),可能其他線程插入了內(nèi)容導(dǎo)致不是最后的節(jié)點(diǎn)。
if (t != tail)
continue;
// 如果不是最后節(jié)點(diǎn)的話將其現(xiàn)在t.next節(jié)點(diǎn)作為tail尾節(jié)點(diǎn)。
if (tn != null) {
advanceTail(t, tn);
continue;
}
// 如果當(dāng)前節(jié)點(diǎn)不是尾節(jié)點(diǎn)進(jìn)入到這里面。
if (s != t) { // If not tail, try to unsplice
// 獲取當(dāng)前節(jié)點(diǎn)(被取消的節(jié)點(diǎn))的下一個(gè)節(jié)點(diǎn)。
QNode sn = s.next;
// 修改上一個(gè)節(jié)點(diǎn)的next(下一個(gè))元素為下下個(gè)節(jié)點(diǎn)。
if (sn == s || pred.casNext(s, sn))
return;
}
QNode dp = cleanMe;
// 嘗試清除上一個(gè)標(biāo)記為清除的節(jié)點(diǎn)
if (dp != null) { // Try unlinking previous cancelled node
//1.獲取要被清除的節(jié)點(diǎn)
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
}
}
E transfer(E e, boolean timed, long nanos) {
QNode s = null; // constructed/reused as needed
// 標(biāo)識(shí)此次操作是存數(shù)據(jù)(put)還是取數(shù)據(jù)(take)
boolean isData = (e != null);
// 自旋匹配節(jié)點(diǎn)
for (;;) {
QNode t = tail;
QNode h = head;
// 如果頭節(jié)點(diǎn)或尾節(jié)點(diǎn)為空繼續(xù)自旋(在TransferQueue初始化的時(shí)候已經(jīng)賦值頭尾結(jié)點(diǎn))
if (t == null || h == null) // saw uninitialized value
continue; // spin
// h == t 說明頭尾結(jié)點(diǎn)相同,是空隊(duì)列
// t.isData == isData 說明尾節(jié)點(diǎn)與當(dāng)前操作一樣
if (h == t || t.isData == isData) { // empty or same-mode
QNode tn = t.next;
// 如果臨時(shí)變量 t 不等于尾節(jié)點(diǎn),說明有其它線程改變了尾節(jié)點(diǎn),則重新自旋匹配節(jié)點(diǎn)
if (t != tail) // inconsistent read
continue;
// 如果尾節(jié)點(diǎn)之后的節(jié)點(diǎn)值不為空,說明也是有其它線程改變了尾節(jié)點(diǎn),將tn節(jié)點(diǎn)賦值給尾節(jié)點(diǎn)
if (tn != null) { // lagging tail
advanceTail(t, tn);
continue;
}
//超時(shí)直接返回 null
if (timed && nanos <= 0)
return null;
// 創(chuàng)建node節(jié)點(diǎn)
if (s == null)
s = new QNode(e, isData);
// 將新創(chuàng)建的node節(jié)點(diǎn)添加到隊(duì)列尾部,如果失敗則重新自旋
if (!t.casNext(null, s))
continue;
// 更新新創(chuàng)建節(jié)點(diǎn)為尾節(jié)點(diǎn)
advanceTail(t, s);
// 調(diào)用 awaitFulfill 方法自旋匹配等待節(jié)點(diǎn)
Object x = awaitFulfill(s, e, timed, nanos);
// 如果返回當(dāng)前節(jié)點(diǎn),則說明節(jié)點(diǎn)由于被取消、超時(shí)、中斷導(dǎo)致匹配失敗
if (x == s) { // wait was cancelled
// 清除當(dāng)前等待匹配節(jié)點(diǎn)
clean(t, s);
return null;
}
// 判斷節(jié)點(diǎn)是否已從隊(duì)列離開
if (!s.isOffList()) { // not already unlinked
// 嘗試將s節(jié)點(diǎn)設(shè)置為head,移出t
advanceHead(t, s); // unlink if head
if (x != null) // and forget fields
s.item = s;
// 釋放 s 節(jié)點(diǎn)當(dāng)前的等待線程
s.waiter = null;
}
// 返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
return (x != null) ? (E)x : e;
} else {// 隊(duì)列不為空,并且當(dāng)前操作與尾節(jié)點(diǎn)的操作不一致。所以當(dāng)前操作與尾節(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 == (x != null):判斷isData與x的模式是否相同,相同表示已經(jīng)完成匹配,繼續(xù)自旋
// x == m :m節(jié)點(diǎn)被取消了
// !m.casItem(x, e):如果嘗試將數(shù)據(jù)e設(shè)置到m上失敗
if (isData == (x != null) || // m already fulfilled
x == m || // m cancelled
!m.casItem(x, e)) { // lost CAS
// 將m設(shè)置為頭結(jié)點(diǎn),h出列,然后重試
advanceHead(h, m); // dequeue and retry
continue;
}
// 成功匹配了,節(jié)點(diǎn)m 設(shè)置為頭結(jié)點(diǎn),h出列
advanceHead(h, m); // successfully fulfilled
// 喚醒節(jié)點(diǎn) m 的等待線程
LockSupport.unpark(m.waiter);
// 返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
return (x != null) ? (E)x : e;
}
}
}
OMG!源碼又是那么長(zhǎng)。但其實(shí)也不是很難,知道一個(gè)規(guī)律就行:由于是使用隊(duì)列作為公平策略,所以在put的時(shí)候會(huì)在隊(duì)列尾部添加數(shù)據(jù),而take的時(shí)候會(huì)從隊(duì)列尾部向隊(duì)列頭部方向?qū)ふ业谝粋€(gè)被阻塞的線程,這樣就可以保證公平的、按順序的釋放被阻塞的線程。
先簡(jiǎn)單的總結(jié)一下核心流程(TransferQueue.trasnfer方法):
1.獲取當(dāng)前操作是存數(shù)據(jù)還是取數(shù)據(jù)
2.自旋尋找匹配的節(jié)點(diǎn)(put操作匹配take操作、take操作匹配put操作)
3.如果頭節(jié)點(diǎn)或尾節(jié)點(diǎn)為空,則繼續(xù)執(zhí)行第2步
4.如果是空隊(duì)列或尾節(jié)點(diǎn)和當(dāng)前操作一樣,執(zhí)行第5步,否則執(zhí)行第13步
5.如果尾節(jié)點(diǎn)被其它線程更改,重新執(zhí)行第2步,否則執(zhí)行第6步
6.如果尾節(jié)點(diǎn)的后繼節(jié)點(diǎn)不為空,說明有其它線程更改,則設(shè)置后繼節(jié)點(diǎn)為尾節(jié)點(diǎn),并重新執(zhí)行第2步;否則執(zhí)行第7步
7.如果超時(shí)直接返回null,否則往下執(zhí)行第8步
8.為當(dāng)前操作創(chuàng)建節(jié)點(diǎn)并添加到隊(duì)列尾部,如果添加成功往下執(zhí)行第9步,否則執(zhí)行第2步
9.自旋匹配等待節(jié)點(diǎn),當(dāng)返回節(jié)點(diǎn)與當(dāng)前節(jié)點(diǎn)不一樣,說明節(jié)點(diǎn)匹配成功,執(zhí)行第10步;否則說明由于被取消、超時(shí)、中斷導(dǎo)致匹配失敗,則清除當(dāng)前節(jié)點(diǎn)并返回null
10.如果節(jié)點(diǎn)已從隊(duì)列離開,執(zhí)行第11步,否則執(zhí)行第12步
11.返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
12.將節(jié)點(diǎn)從隊(duì)列中移除,并重新設(shè)置隊(duì)列頭節(jié)點(diǎn)后執(zhí)行第11步
13.執(zhí)行到這一步,說明當(dāng)前操作與尾節(jié)點(diǎn)的操作是互相匹配;那么如果隊(duì)列頭節(jié)點(diǎn)是否匹配完成或隊(duì)列頭節(jié)點(diǎn)被取消,又或者cas更新頭節(jié)點(diǎn)操作失敗,則執(zhí)行第14步,否則執(zhí)行第15步
14.重新設(shè)置頭節(jié)點(diǎn)并執(zhí)行第2步
15.執(zhí)行到這一步代表匹配成功,重新設(shè)置隊(duì)列的頭節(jié)點(diǎn),并喚醒頭節(jié)點(diǎn)的等待線程,最后返回節(jié)點(diǎn)值(put返回put操作的值,take返回匹配到的節(jié)點(diǎn)值)
還是依照個(gè)人習(xí)慣,喜歡通過畫圖分析一下源碼流程!
總結(jié):
- SynchronousQueue 是一個(gè)沒有隊(duì)列大小的概念,所有的操作都必須與其匹配的節(jié)點(diǎn)共同入隊(duì)出隊(duì)(公平模式)或入棧出棧(非公平模式)
- SynchronousQueue 是輕量級(jí)的阻塞隊(duì)列。因?yàn)镾ynchronousQueue是沒有使用到鎖,都是通過CAS方法保證線程安全
其實(shí)也不難發(fā)現(xiàn),SynchronousQueue 的缺點(diǎn)也是十分明顯。如果同一個(gè)模式的節(jié)點(diǎn)多的話,就會(huì)一直阻塞,這是會(huì)損耗性能,所以需要根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景使用。
最后希望讀者們看源碼的時(shí)候,親自debug,這樣才會(huì)加深源碼的理解,讀任何文章都只是輔助,自己真正理解才是學(xué)會(huì)東西。
如果覺得源碼剖析不錯(cuò)的話,麻煩點(diǎn)個(gè)贊哈!對(duì)于文章有哪里不清楚或者有誤的地方,歡迎在評(píng)論區(qū)留言~
參考資料:
https://www.cnblogs.com/dwlsxj/p/Thread.html
慕課網(wǎng):面試官系統(tǒng)精講Java源碼及大廠真題: https://www.imooc.com/read/47/article/862