07 併發工具類CountDownLatch、CyclicBarrier、Semaphore使用及原始碼分析
在 JUC 下包含了一些常用的同步工具類,今天就來詳細介紹一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它們之間的區別。
1 CountDownLatch
1.1 功能描述
CountDownLatch是一個同步工具類,用來協調多個執行緒之間的同步。它允許一個或多個執行緒一直等待,直到其他執行緒的操作執行完畢再執行
CountDownLatch能夠使一個執行緒在等待另外一些執行緒完成各自工作之後,再繼續執行。使用一個計數器進行實現。計數器初始值為執行緒的數量。當每一個執行緒完成自己任務後,計數器的值就會減一。當計數器的值為0時,表示所有的執行緒都已經完成任務,然後在CountDownLatch上等待的執行緒就可以恢復執行接下來的任務。
countdownlatch 提供了兩個方法,一個是 countDown,一個是 await,countdownlatch初始化的時候需要傳入一個整數,在這個整數倒數到 0之前,呼叫了 await 方法的程式都必須要等待,然後通過 countDown 來倒數。
1.2 簡單案例
public class CountDownLatchDemo {
private static final CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) {
try {
System.out.println("main執行緒開始執行");
for (int i = 0; i < 3; i++) {
new Thread(new CountDownLatchThread(), String.valueOf(i)).start();
TimeUnit.SECONDS.sleep(1);
}
// 阻塞,直到countDownLatch為0
// main執行緒等待3個子執行緒都執行完成會被喚醒
countDownLatch.await();
System.out.println("main執行緒執行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class CountDownLatchThread implements Runnable {
@Override
public void run() {
try {
System.out.println("子執行緒" + Thread.currentThread().getName() + "開始執行");
TimeUnit.SECONDS.sleep(10);
// 當前執行緒呼叫此方法,則計數減一
countDownLatch.countDown();
System.out.println("子執行緒" + Thread.currentThread().getName() + "執行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
程式功能:main執行緒需要等待其他3個子執行緒執行結束後,才會繼續執行後續的程式碼。有點類似 join 的功能,但是比 join 更加靈活。
join的功能
:在當前執行緒中,如果呼叫某個thread的join方法,那麼當前執行緒就會被阻塞,直到thread執行緒執行完畢,當前執行緒才能繼續執行。join的原理是,不斷的檢查thread是否存活,如果存活,那麼讓當前執行緒一直wait,直到thread執行緒終止,執行緒的this.notifyAll 就會被呼叫。
與join的區別
:假設子執行緒的工作都分為兩個階段,主執行緒只需要等待子執行緒完成第一個階段就可以執行了。使用join是無法實現的,此時使用countDownLatch改變countDown()的位置即可。
1.3 原始碼分析
1.3.1 類圖UML
它的底層實現是基於 AQS 的共享鎖。
1.3.2 初始化
CountDownLatch 使用了共享鎖模式。CountDownLatch 使用了一個內部類 Sync來實現CountDownLatch的同步控制,而Sync是AQS的一個實現類,它使用AQS的狀態(state)來表示count。
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
1.3.3 await()
導致當前的執行緒等待直到count被倒數到0,或者執行緒被中斷。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// 執行緒被中斷,丟擲中斷異常
if (Thread.interrupted())
throw new InterruptedException();
// 嘗試獲取共享鎖
if (tryAcquireShared(arg) < 0)
// 獲取鎖失敗,加入等待佇列
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared只有當State為0才可以獲取到同步鎖
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
doAcquireSharedInterruptibly:獲取共享鎖失敗加入等待佇列(可中斷),程式碼同ReentrantReadWriteLock中的ReadLoack,詳見之前的程式碼分析。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
1.3.4 countDown()
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// 釋放共享鎖,當釋放後State為0。喚醒共享鎖的同步佇列中等待的執行緒。
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
2 CyclicBarrier
2.1 功能描述
作用
:讓所有執行緒都等待完成後才會繼續下一步行動。
柵欄類似於閉鎖,它能阻塞一組執行緒直到某個事件的發生。柵欄與閉鎖的關鍵區別在於,所有的執行緒必須同時到達柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其他執行緒。
CyclicBarrier可以使一定數量的執行緒反覆地在柵欄位置處彙集。當執行緒到達柵欄位置時將呼叫await方法,這個方法將阻塞直到所有執行緒都到達柵欄位置。如果所有執行緒都到達柵欄位置,那麼柵欄將開啟,此時所有的執行緒都將被釋放,而柵欄將被重置以便下次使用。
2.2 簡單案例
public class CyclicBarrierDemo {
private static final CyclicBarrier cyclicBarrier = new CyclicBarrier(4, new BarrierAction());
public static void main(String[] args) throws Exception {
for (int i = 0; i < 4; i++) {
new Thread(new BarrierThread(), String.valueOf(i)).start();
TimeUnit.MILLISECONDS.sleep(1);
}
}
static class BarrierThread implements Runnable {
@Override
public void run() {
try {
System.out.println("執行緒: " + Thread.currentThread().getName() + " 啟動");
TimeUnit.SECONDS.sleep(5);
cyclicBarrier.await();
System.out.println("執行緒: " + Thread.currentThread().getName() + " 結束");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
/**
* CyclicBarrier可以再次使用
*/
static class CyclicBarrierThread implements Runnable {
@Override
public void run() {
try {
System.out.println("執行緒: " + Thread.currentThread().getName() + " 啟動");
cyclicBarrier.await();
System.out.println("執行緒: " + Thread.currentThread().getName() + " 結束");
TimeUnit.SECONDS.sleep(10);
System.out.println("執行緒: " + Thread.currentThread().getName() + " 再次啟動");
cyclicBarrier.await();
System.out.println("執行緒: " + Thread.currentThread().getName() + " 再次結束");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
static class BarrierAction implements Runnable {
@Override
public void run() {
System.out.println("柵欄都到達後,第一個先執行的執行緒");
}
}
}
執行緒: 0 啟動
執行緒: 1 啟動
執行緒: 2 啟動
執行緒: 3 啟動
柵欄都到達後,第一個先執行的執行緒
執行緒: 3 結束
執行緒: 0 結束
執行緒: 2 結束
執行緒: 1 結束
把parties改為2後的輸出結果是什麼呢?
先執行兩個後再執行另外兩個。分兩批執行。這個就是柵欄的迴圈使用
執行緒: 0 啟動
執行緒: 1 啟動
執行緒: 2 啟動
執行緒: 3 啟動
柵欄都到達後,第一個先執行的執行緒
執行緒: 1 結束
執行緒: 0 結束
柵欄都到達後,第一個先執行的執行緒
執行緒: 3 結束
執行緒: 2 結束
2.3 原始碼分析
底層使用ReentrantLock 和 Condition來實現的。
2.3.1 初始化
/** The lock for guarding barrier entry */
// 可重入獨佔鎖
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
// 等待佇列
private final Condition trip = lock.newCondition();
/** The number of parties */
// 參與的執行緒數量
private final int parties;
/* The command to run when tripped */
// 由最後一個進入 barrier 的執行緒執行的操作
private final Runnable barrierCommand;
/** The current generation */
// 當前代
private Generation generation = new Generation();
// 正在等待進入屏障的執行緒數量
private int count;
private static class Generation {
boolean broken = false;
}
CyclicBarrier中主要的成員變數。
- CyclicBarrier 使用 ReentrantLock 和 Condition 類來構建。之前的原始碼分析已經分析了這兩個類,具體可以參考之前的文章。
- CyclicBarrier 類存在一個內部類 Generation,每一次使用 CyclicBarrier 可以當成 Generation 的例項。
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
// 參與的執行緒數量小於等於0,丟擲異常
if (parties <= 0) throw new IllegalArgumentException();
// 設定parties
this.parties = parties;
// 設定count
this.count = parties;
// 設定barrierCommand
this.barrierCommand = barrierAction;
}
CyclicBarrier有兩個建構函式
CyclicBarrier 預設的構造方法是 CyclicBarrier(int parties),其參數列示柵欄攔截的執行緒數量,每個執行緒呼叫 await 方法告訴 CyclicBarrier 當前執行緒已經到達了柵欄,然後當前執行緒被阻塞。
CyclicBarrier的另一個建構函式CyclicBarrier(int parties, Runnable barrierAction),用於執行緒到達柵欄時,優先執行barrierAction,方便處理更復雜的業務場景。由最後一個進入 barrier 的執行緒執行的操作。
2.3.2 await
呼叫await方法的執行緒告訴CyclicBarrier自己已經到達同步點,然後當前執行緒被阻塞,直到parties個參與執行緒呼叫了await方法。CyclicBarrier同樣提供帶超時時間的await和不帶超時時間的await方法。
超時時間的await,時間到達後。會先破壞柵欄,之後喚醒所有的執行緒。
public int await() throws InterruptedException, BrokenBarrierException {
try {
// 不超時等待
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
這兩個方法最終都會呼叫dowait(boolean, long)方法,它也是CyclicBarrier的核心方法
2.3.3 dowait
dowait(boolean, long)方法的主要邏輯處理比較簡單,如果該執行緒不是最後一個呼叫await方法的執行緒,則它會一直處於等待狀態,除非發生以下情況:
- 最後一個執行緒到達,即index == 0
- 某個參與執行緒等待超時
- 某個參與執行緒被中斷
- 呼叫了CyclicBarrier的reset()方法。該方法會將屏障重置為初始狀態
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
// 獲取獨佔鎖
final ReentrantLock lock = this.lock;
// 當前執行緒鎖定
lock.lock();
try {
// 當前代
final Generation g = generation;
// 如果這代損壞了,丟擲異常
if (g.broken)
throw new BrokenBarrierException();
// 如果執行緒中斷了,丟擲異常
if (Thread.interrupted()) {
// 將損壞狀態設定為true
// 並通知其他阻塞在此柵欄上的執行緒
breakBarrier();
throw new InterruptedException();
}
// 減少正在等待進入柵欄的執行緒數量
// 正在等待進入柵欄的執行緒數量
int index = --count;
// 正在等待進入柵欄的執行緒數量為0,所有執行緒都已經進入
if (index == 0) { // tripped
// 執行的動作標識
boolean ranAction = false;
try {
// 初始化時的執行動作
final Runnable command = barrierCommand;
// 執行動作不為空
if (command != null)
// 執行
command.run();
// 設定ranAction狀態
ranAction = true;
// 進入下一代,迴圈使用的重要原理
nextGeneration();
return 0;
} finally {
// 上面發生異常
if (!ranAction)
// 損壞當前屏障
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
// 無限迴圈
for (;;) {
try {
// 沒有設定等待時間
if (!timed)
// 當前執行緒等待,加入等待佇列中
trip.await();
// 置等待時間
else if (nanos > 0L)
// 當前執行緒等待,加入等待佇列中。超時後當前執行緒自動喚醒
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) { // 執行緒等待await中被中斷
// 等於當前代並且柵欄沒有被損壞
if (g == generation && ! g.broken) {
// 損壞當前柵欄
breakBarrier();
// 丟擲異常
throw ie;
} else {
// 上面條件不滿足,說明這個執行緒不是這代的
// 就不會影響當前這代柵欄的執行,所以,就打個中斷標記
// 中斷當前執行緒
Thread.currentThread().interrupt();
}
}
// 當有任何一個執行緒中斷了,就會呼叫breakBarrier方法
// 就會喚醒其他的執行緒,其他執行緒醒來後,也要丟擲異常
if (g.broken)
throw new BrokenBarrierException();
// 不等於當前代
// g != generation表示正常換代了,返回當前執行緒所在柵欄的下標
// 如果 g == generation,說明還沒有換代,那為什麼會醒了?
// 因為一個執行緒可以使用多個柵欄,當別的柵欄喚醒了這個執行緒,就會走到這裡,所以需要判斷是否是當前代。
// 正是因為這個原因,才需要generation來保證正確。
if (g != generation)
// 正在等待進入柵欄的執行緒數量
return index;
// 設定了等待時間,並且等待時間小於0
// await(long timeout, TimeUnit unit)
// 柵欄破壞,喚醒所有await的執行緒
if (timed && nanos <= 0L) {
// 損壞柵欄
breakBarrier();
// 丟擲異常
throw new TimeoutException();
}
}
} finally {
// 釋放鎖
lock.unlock();
}
}
我們可能需要注意Generation 物件,在上述程式碼中我們總是可以看到丟擲BrokenBarrierException異常,那麼什麼時候丟擲異常呢?如果一個執行緒處於等待狀態時,如果其他執行緒呼叫reset(),或者呼叫的barrier原本就是被損壞的,則丟擲BrokenBarrierException異常。同時,任何執行緒在等待時被中斷了,則其他所有執行緒都將丟擲BrokenBarrierException異常,並將barrier置於損壞狀態。
同時,Generation描述著CyclicBarrier的更新換代。在CyclicBarrier中,同一批執行緒屬於同一代。當有parties個執行緒到達barrier之後,generation就會被更新換代。其中broken標識該當前CyclicBarrier是否已經處於中斷狀態。
2.3.4 breakBarrier
當barrier損壞了或者有一個執行緒中斷了,則通過breakBarrier()來終止所有的執行緒。
在breakBarrier()中除了將broken設定為true,還會呼叫signalAll將在CyclicBarrier處於等待狀態的執行緒全部喚醒。
private void breakBarrier() {
// 設定狀態
generation.broken = true;
// 恢復正在等待進入柵欄的執行緒數量
count = parties;
// 喚醒所有執行緒
trip.signalAll();
}
2.3.5 nextGeneration
當所有執行緒都已經到達barrier處(index == 0),則會通過nextGeneration()進行更新換地操作,在這個步驟中,做了三件事:喚醒所有執行緒,重置count,generation
private void nextGeneration() {
// signal completion of last generation
// 喚醒所有執行緒
trip.signalAll();
// set up next generation
// 重置正在等待進入屏障的執行緒數量
count = parties;
// 新生一代
generation = new Generation();
}
除了上面講到的柵欄更新換代以及損壞狀態,我們在使用CyclicBarrier時還要要注意以下幾點:
- CyclicBarrier使用獨佔鎖來執行await方法,併發性可能不是很高
- 如果在等待過程中,執行緒被中斷了,就丟擲異常。但如果中斷的執行緒所對應的CyclicBarrier不是這代的,比如,在最後一次執行緒執行signalAll後,並且更新了這個“代”物件。在這個區間,這個執行緒被中斷了,那麼,JDK認為任務已經完成了,就不必在乎中斷了,只需要打個標記。該部分原始碼已在dowait(boolean, long)方法中進行了註釋。
- 如果執行緒被其他的CyclicBarrier喚醒了,那麼g肯定等於generation,這個事件就不能return了,而是繼續迴圈阻塞。反之,如果是當前CyclicBarrier喚醒的,就返回執行緒在CyclicBarrier的下標。完成了一次衝過柵欄的過程。該部分原始碼已在dowait(boolean, long)方法中進行了註釋。
3 Semaphore
3.1 功能描述
semaphore 也就是我們常說的訊號燈,semaphore 可以控制同時訪問的執行緒個數,通過 acquire 獲取一個許可,如果沒有就等待,通過 release 釋放一個許可。有點類似限流的作用。叫訊號燈的原因也和他的用處有關,比如某商場就 5 個停車位,每個停車位只能停一輛車,如果這個時候來了 10 輛車,必須要等前面有空的車位才能進入。
3.2 簡單案例
public class SemaphoreDemo {
private static Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(new ParkCar(), "車輛 " + i).start();
TimeUnit.MILLISECONDS.sleep(100);
}
}
static class ParkCar implements Runnable {
@Override
public void run() {
try {
// 申請許可證類似於檢視有無車位
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 開始停車");
// 模擬停車10s中
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 結束停車");
// 釋放許可證類似於車子駛離停車場,空出來了車位
semaphore.release();
}
}
}
輸出結果:
一次聽了5輛車,當後面車子到來時,停車場無空的位子,需要等待前面的車輛駛離停車場,駛離一輛,後面就可以加入一輛車子。
車位一共就發 5 個,那等第一批車輛用完釋放之後, 第二批的時候應該發給誰呢?
所有等待的車輛都想先拿到許可,先通行,怎麼辦。這就需要,用到鎖了。就所有人都去搶,誰先搶到,誰就先停車。
車輛 0 開始停車
車輛 1 開始停車
車輛 2 開始停車
車輛 3 開始停車
車輛 4 開始停車
車輛 0 結束停車
車輛 5 開始停車
車輛 1 結束停車
車輛 6 開始停車
車輛 2 結束停車
車輛 7 開始停車
車輛 3 結束停車
車輛 8 開始停車
車輛 4 結束停車
車輛 9 開始停車
車輛 5 結束停車
車輛 6 結束停車
車輛 7 結束停車
車輛 8 結束停車
車輛 9 結束停車
3.3 原始碼分析
3.3.1 UML類圖
它的底層實現是基於 AQS 的共享鎖。
3.3.2 初始化
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
從構造器裡面可以看出來semaphore預設實現的是非公平鎖,當然我們也可以指定鎖的型別,是否為公平鎖。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
Sync(int permits) {
setState(permits);
}
protected final void setState(int newState) {
state = newState;
}
我們可以看到NonfairSync類繼承了Sync,而Sync繼承了AQS,從這裡其實可以看出來semaphore是基於AQS實現的。AQS中的同步狀態值state儲存許可證的個數
以下程式碼以非公平鎖為例
3.3.3 acquire
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
// 如果執行緒中斷則丟擲異常
if (Thread.interrupted())
throw new InterruptedException();
// 申請共享鎖
if (tryAcquireShared(arg) < 0)
// 申請共享鎖失敗,加入等待佇列
doAcquireSharedInterruptibly(arg);
}
3.3.4 tryAcquireShared
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
// 自旋
for (;;) {
// 獲取許可數
int available = getState();
// 剩餘許可數
int remaining = available - acquires;
// 剩餘許可數大於0,當前執行緒可以執行。CAS修改許可數
// 剩餘許可數小於0,加入同步佇列等待。
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
3.3.5 doAcquireSharedInterruptibly
許可數用完,加入同步佇列。等待許可的釋放。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
前面分析ReentrantReadWriteLock.ReadLock詳細分析過。
3.3.6 release
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
此處sync呼叫了AQS中的方法releaseShared,在這個方法中如果釋放成功那麼就呼叫doReleaseShared方法,此方法在前面AQS共享模式一文中已經講解過,此處不在詳細講解。它主要作用就是釋放佇列中的節點。
protected final boolean tryReleaseShared(int releases) {
for (;;) {
// 獲取當前許可數
int current = getState();
// 釋放後的許可數
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
// 更新新的許可數
if (compareAndSetState(current, next))
return true;
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
相關文章
- Java 併發工具類 CountDownLatch、CyclicBarrier、Semaphore、ExchangerJavaCountDownLatch
- java多執行緒10:併發工具類CountDownLatch、CyclicBarrier和SemaphoreJava執行緒CountDownLatch
- CyclicBarrier、CountDownLatch以及Semaphore使用及其原理分析CountDownLatch
- 併發-6-wait、notify、Semaphore、CountDownLatch、CyclicBarrierAICountDownLatch
- JUC併發程式設計之Semaphore、CountDownLatch、CyclicBarrier協同工具程式設計CountDownLatch
- 大白話說java併發工具類-CountDownLatch,CyclicBarrierJavaCountDownLatch
- 強大的三個工具類、CountDownLatch 閉鎖 、CyclicBarrier 、SemaphoreCountDownLatch
- 併發工具類:Semaphore原始碼解讀原始碼
- 【高併發】AQS中的CountDownLatch、Semaphore與CyclicBarrier用法總結AQSCountDownLatch
- Java併發包5--同步工具CountDownLatch、CyclicBarrier、Semaphore的實現原理解析JavaCountDownLatch
- 併發模擬-程式碼CountDownLatch,SemaphoreCountDownLatch
- Java併發—— CountDownLatch與CyclicBarrierJavaCountDownLatch
- 併發工具類——Semaphore
- CountDownLatch、CyclicBarrier、Semaphore、Exchanger 的詳細解析CountDownLatch
- Java 併發包之CountDownLatch、CyclicBarrierJavaCountDownLatch
- 高併發之ReentrantLock、CountDownLatch、CyclicBarrierReentrantLockCountDownLatch
- 併發程式設計之 CyclicBarrier 原始碼分析程式設計原始碼
- Java併發系列—工具類:SemaphoreJava
- Java併發(6)- CountDownLatch、Semaphore與AQSJavaCountDownLatchAQS
- 併發工具類(二)同步屏障CyclicBarrier
- 面試官:說說CountDownLatch,CyclicBarrier,Semaphore的原理?面試CountDownLatch
- Java 併發程式設計(十四) -- CyclicBarrier原始碼分析Java程式設計原始碼
- Java 併發程式設計(十三) -- CountDownLatch原始碼分析Java程式設計CountDownLatch原始碼
- Java 併發程式設計(十五) -- Semaphore原始碼分析Java程式設計原始碼
- JUC併發系列(八):併發程式設計常用輔助類CountDownLatch與CyclicBarrier(手敲程式碼示例)程式設計CountDownLatch
- 併發類Condition原始碼分析原始碼
- CyclicBarrier原始碼分析原始碼
- 我們們來聊聊併發工具類Semaphore
- 大白話說java併發工具類-Semaphore,ExchangerJava
- Redisson 分散式鎖原始碼 11:Semaphore 和 CountDownLatchRedis分散式原始碼CountDownLatch
- CountDownLatch原始碼分析CountDownLatch原始碼
- Semaphore原始碼分析原始碼
- Java併發之Semaphore原始碼解析(一)Java原始碼
- Java併發之Semaphore原始碼解析(二)Java原始碼
- CountDownLatch和CyclicBarrier區別及詳解CountDownLatch
- 併發工具類(一)等待多執行緒的CountDownLatch執行緒CountDownLatch
- CountDownLatch 概述和原始碼分析CountDownLatch原始碼
- JUC之CountDownLatch原始碼分析CountDownLatch原始碼