CountDownLatch(閉鎖)、Semaphore(信號量)、CyclicBarrier(迴環柵欄)

一、CountDowLatch(閉鎖)(倒計數鎖存器)

  CountDownLatch類位於java.util.concurrent包下,在完成某些運算時,只有其他所有線程的運算全部完成,當前運算才繼續執行。

  利用它可以實現類似計數器的功能。比如有一個任務A,它要等待其他4個任務執行完畢之後才能執行,此時就可以利用CountDownLatch來實現這種功能。

final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
    public void run() {
        System.out.println("子線程"+Thread.currentThread().getName()+"正在執行");
        Thread.sleep(3000);
        System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢");
        latch.countDown();
    };
}.start();
new Thread(){ 
    public void run() {
        System.out.println("子線程"+Thread.currentThread().getName()+"正在執行");
        Thread.sleep(3000);
        System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢");
        latch.countDown();
    };
}.start();
System.out.println("等待 2 個子線程執行完畢...");
latch.await();
System.out.println("2 個子線程已經執行完畢");
System.out.println("繼續執行主線程");
}

二、Semaphore(信號量-控制同時訪問的線程個數)

  信號量可以控制同時訪問的線程個數,通過acquire()獲取一個許可,如果沒有就等待,而通過release()釋放一個許可。

Semaphore類中比較重要的幾個方法:

  1)public void acquire():用來獲取一個許可,若無許可能夠獲得,則會一直等待,知道獲得許可。

  2)public void acquire(int permits):獲取permits個許可。

  3)public void release(){}:釋放許可,注意,在釋放許可之前,必須先獲得許可。

  4)public void release(int permits){}:釋放permits個許可。

上面4個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法:

  1)public boolean tryAcquire():嘗試獲取一個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false。

  2)public boolean tryAcquire(long timeout,TimeUnit unit):嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回true,否則立即返回false。

  3)public boolean tryAcquire(int permits):嘗試獲取permits個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false。

  4)public boolean tryAcquire(int permits,long timeout,TimeUnit unit):嘗試獲取permits個許可,若在指定的時間內獲取成功,則立即返回true,否則立即返回false。

  5)還可以通過availablePermits()方法得到可用的許可數目。

  例子:若一個工廠有5臺機器,一臺機器同時只能被一個工人使用,只有使用完了,其他工人才能繼續使用。那麼我們就可以通過Semaphore來實現:

int N = 8; //工人數
Semaphore semaphore = new Semaphore(5); //機器數目
for(int i=0;i<N;i++){
    new Worker(i,semaphore).start();
}
static class Worker extends Thread{
    private int num;
    private Semaphore semaphore;
    public Worker(int num,Semaphore semaphore){
        this.num = num;
        this.semaphore = semaphore;
    }
    public void run() {
        try {
            semaphore.acquire();
            System.out.println("工人"+this.num+"佔用一個機器在生產...");
            Thread.sleep(2000);
            System.out.println("工人"+this.num+"釋放出機器");
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

三、CyclicBarrier(迴環柵欄-等待至barrier狀態再全部同時執行)

  迴環柵欄,通過它可以實現讓一組線程等待至某個狀態之後再全部同時執行,叫做迴環,是因爲當所有等待線程都被釋放以後,CyclicBarrier可以被重用。我們暫且把這種狀態叫做barrier,當調用await()方法之後,線程就處於barrier了。

  CyclicBarrier中最重要的方法就是await方法,它有兩個重載版本:

    1)public int await():用來掛起當前線程,直至所有線程都到達barrier狀態再同時執行後續任務。

    2)public int await(long timeout,TimeUnit unit):讓這些線程等待至一定的時間,如果還有線程沒有到達barrier狀態就直接讓到達barrier的線程執行後續任務。

  具體使用如下:

public static void main(String[] args) {
     int N = 4;
     CyclicBarrier barrier = new CyclicBarrier(N);
     for(int i=0;i<N;i++)
         new Writer(barrier).start();
     }
 static class Writer extends Thread{
     private CyclicBarrier cyclicBarrier;
     public Writer(CyclicBarrier cyclicBarrier) {
         this.cyclicBarrier = cyclicBarrier;
     }
 public void run() {
     try {
         Thread.sleep(5000); //以睡眠來模擬線程需要預定寫入數據操作
System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其他線程寫入完畢");
         cyclicBarrier.await();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }catch(BrokenBarrierException e){
         e.printStackTrace();
     }
     System.out.println("所有線程寫入完畢,繼續處理其他任務,比如數據操作");
     }
 }

三者之間的聯繫與區別:

  CountDownLatch和CyclicBarrier都能夠實現線程之間的等待,只不過它們側重點不同。

       CountDownLatch一般用於某個線程A等待若干個線程執行完任務之後,它才執行;而CyclicBarrier一般用於一組線程互相等待至某個狀態,然後這一組線程再同時執行。

       另外,CountDownLatch是不能夠重用的,而CyclicBarrier是可以重用的。

  Semaphore其實和鎖有點類似,它一般用於控制對某組資源的訪問權限。

------------------------

參考:https://www.cnblogs.com/HuiH/p/11904898.html

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