JUC(3)---CountDownLatch、CyclicBarrier和AQS

CountDownLatch:

  可以讓一個線程等待其他線程完成了各自的工作之後再執行。比如說一個切菜,一個人切肉,都準備完畢之後才能炒肉。

構造方法

public CountDownLatch(int count)  count等待的線程數量

關鍵API

countDown()   分線程執行完減少計數

await()        主線程等待調用

使用

package com.nijunyang.concurrent;

import java.util.concurrent.CountDownLatch;

/**
 * Description:
 * Created by nijunyang on 2020/5/16 13:53
 */
public class CountDownLatchTest{

    private CountDownLatch countDownLatch;

    public CountDownLatchTest(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    public static void main(String[] args) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(2);
        CountDownLatchTest countDownLatchTest = new CountDownLatchTest(countDownLatch);

        new Thread(()-> {
            try {
                countDownLatchTest.method1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"線程1").start();
        new Thread(()-> {
            try {
                countDownLatchTest.method2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"線程2").start();

        System.out.println("等待食材準備完畢...");
        countDownLatch.await();
        System.out.println("炒肉...");


//        System.out.println("------第二次使用-----");
//        new Thread(()-> {
//            try {
//                countDownLatchTest.method1();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        },"線程1").start();
//        new Thread(()-> {
//            try {
//                countDownLatchTest.method2();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        },"線程2").start();
//
//        System.out.println("等待食材準備完畢...");
//        countDownLatch.await();
//        System.out.println("炒肉...");

    }

    private void method1() throws InterruptedException {
        Thread.sleep(5000L);
        System.out.println("切菜完畢...");
        countDownLatch.countDown();
    }

    private void method2() throws InterruptedException {
        Thread.sleep(10000L);
        System.out.println("切肉完畢...");
        countDownLatch.countDown();
    }
}

 

原理解析

1.從構造方法進去我們可以看到又是一個熟悉的Sync內部類繼承了AbstractQueuedSynchronizer,入參的數量被賦值到AbstractQueuedSynchronizer的state字段。

2.await方法會去判斷state是否等於0,如果不等於0,說明其他線程還沒有執行完畢。就會執行doAcquireSharedInterruptibly這個方法,將當前這個調用await方法的線程入隊阻塞。

(調用鏈:await()-sync.acquireSharedInterruptibly-sync.tryAcquireShared-doAcquireSharedInterruptibly)

3.countDown方法,每調一次就會將state的值減1,當扣減到0的時候去喚醒上面等待的主線程執行(調用鏈:countDown-sync.releaseShared-sync.tryReleaseShared-doReleaseShared(減到0纔會執行這方法))

 

 

 

 

 

CyclicBarrier

籬柵,顧名思義有攔截作用。它可以讓一組線程到達柵欄時被阻塞,直到最後一個線程到達,才放行通過。比如玩LOL,需要等待所有玩家進度條100%了,才能進入遊戲

構造方法

CyclicBarrier(int parties)      parties:阻塞的線程數量

CyclicBarrier(int parties, Runnable barrierAction)  parties:阻塞的線程數量  barrierAction:當最後一個線程到達是先執行這個任務,再去執行後面的流程。

關鍵API:

await()  到達柵欄點等待。調用次數要和入參數量一致,否則會一致阻塞的等待。

使用

package com.nijunyang.concurrent;

import java.util.concurrent.CyclicBarrier;

/**
 * @author: create by nijunyang
 * @date:2019/9/5
 */
public class CyclicBarrierTest implements Runnable{
    private CyclicBarrier cyclicBarrier;
    public CyclicBarrierTest(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + "進度條100%... ");
            cyclicBarrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(11, new Runnable() {//11個是因爲還有一個主線程也在等待
                    public void run() {
                        System.out.println("所有人進度條100%,準備開始遊戲");
                    }
                });
        for (int i = 0; i < 10; i++) {
            new Thread(new CyclicBarrierTest(cyclicBarrier), "線程" + i).start();
        }
        cyclicBarrier.await();
        Thread.sleep(300);
        System.out.println("開始遊戲....");
    }

}

 

原理解析

1.默認每個CyclicBarrier對象有一把鎖ReentrantLock和Condition

 

2.將構造方法的入參數量賦值到count字段中。後續都是在count字段上面進行操作。

 

3.await的調用會將count的數量-1,如果扣減到0.則會先執行構造方法傳入的任務(如果傳了),並且重置計數器刷新柵欄,將許可數據重新賦值給count字段(可以重複使用),喚醒條件等待的線程

 

 

 

 4.如果扣減完成之後還沒有到0.說明還有線程沒有到達柵欄點。則進入條件隊列阻塞等到,等到最後一個到達時候,才被喚醒

 

 

 兩者比較

CountDownLatch和CyclicBarrier,最終實現效果看起來都差不多,都是等待分支線程執行完畢,再往下執行。然後CyclicBarrier這個可以重複使用,因爲會去刷新count的數量。CountDownLatch不會重新刷新state字段的值。當第二次await執行的時候一看state是0就直接放行了,所以一個CountDownLatch對象只能使用一次。

 

原理上CountDownLatch是阻塞主線程,分支線線程執行完畢將state扣減到0了之後喚醒主線程去執行,CyclicBarrier則是所有線程到達柵欄點都會阻塞等待。直到後一個到達才喚醒所有的阻塞線程。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章