JUC中提供了幾個比較常用的併發工具類,比如CountDownLatch、CyclicBarrier、Semaphore。 其實在以前我們 課堂的演示程式碼中,或多或少都有用到過這樣一些api,接下來我們會帶大家去深入研究一些常用的api。 CountDownLatch
countdownlatch是一個同步工具類,它允許一個或多個執行緒一直等待,直到其他執行緒的操作執行完畢再執行。從 命名可以解讀到countdown是倒數的意思,類似於我們倒數計時的概念。 countdownlatch提供了兩個方法,一個是countDown,一個是await, countdownlatch初始化的時候需要傳入一 個整數,在這個整數倒數到0之前,呼叫了await方法的程式都必須要等待,然後通過countDown來倒數。
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(3);
new Thread(()->{
countDownLatch.countDown();
},"t1").start();
new Thread(()->{
countDownLatch.countDown();
},"t2").start();
new Thread(()->{
countDownLatch.countDown();
},"t3").start();
countDownLatch.await();
System.out.println("所有執行緒執行完畢");
}
從程式碼的實現來看,有點類似join的功能,但是比join更加靈活。CountDownLatch建構函式會接收一個int型別的 引數作為計數器的初始值,當呼叫CountDownLatch的countDown方法時,這個計數器就會減一。 通過await方法去阻塞去阻塞主流程
使用場景
1. 通過countdownlatch實現最大的並行請求,也就是可以讓N個執行緒同時執行,這個我也是在課堂上寫得比較多 的
2. 比如應用程式啟動之前,需要確保相應的服務已經啟動,比如我們之前在講zookeeper的時候,通過原生api連 接的地方有用到countDownLatch 原始碼分析
CountDownLatch類存在一個內部類Sync,上節課我們講過,它是一個同步工具,一定繼承了 AbstractQueuedSynchronizer。很顯然,CountDownLatch實際上是是使得執行緒阻塞了,既然涉及到阻塞,就一 定涉及到AQS佇列 await
await函式會使得當前執行緒在countdownlatch倒數計時到0之前一直等待,除非執行緒別中斷;從原始碼中可以得知await 方法會轉發到Sync的acquireSharedInterruptibly
方法
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } acquireSharedInterruptibly
這塊程式碼主要是判斷當前執行緒是否獲取到了共享鎖; 上一節課提到過,AQS有兩種鎖型別,一種是共享鎖,一種是 獨佔鎖,在這裡用的是共享鎖; 為什麼要用共享鎖,因為CountDownLatch可以多個執行緒同時通過。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
doAcquireSharedInterruptibly
獲取共享鎖
if (Thread.interrupted()) //判斷執行緒是否中斷
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) //如果等於0則返回1,否則返回-1,返回-1表示需要阻塞
doAcquireSharedInterruptibly(arg);
} 在這裡,state的意義是count,如果計數器為0,表示不需要阻塞,否則,只有在滿足條件的情況下才會被喚醒
doAcquireSharedInterruptibly
獲取共享鎖
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED); //建立一個共享模式的節點新增到佇列中 boolean failed = true;
try {
for (;;) { //自旋等待共享鎖釋放,也就是等待計數器等於0。
final Node p = node.predecessor(); //獲得當前節點的前一個節點
if (p == head) {
int r = tryAcquireShared(arg);//就判斷嘗試獲取鎖
if (r >= 0) {//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);
}
}
待續。。。。。。。。。