併發框架

碼魘發表於2019-01-19

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);
 
    }
 
}

待續。。。。。。。。。

相關文章