天天看點

Java并發程式設計的藝術(八)——閉鎖、同步屏障、信号量詳解1. 閉鎖:CountDownLatch 2. 同步屏障:CyclicBarrier 3. 信号量:Semaphore

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qq_34173549/article/details/79612374

1. 閉鎖:CountDownLatch

1.1 使用場景

若有多條線程,其中一條線程需要等到其他所有線程準備完所需的資源後才能運作,這樣的情況可以使用閉鎖。

1.2 代碼實作

// 初始化閉鎖,并設定資源個數
CountDownLatch latch = new CountDownLatch(2);

Thread t1 = new Thread( new Runnable(){
    public void run(){
        // 加載資源1
        加載資源的代碼……
        // 本資源加載完後,閉鎖-1
        latch.countDown();
    }
} ).start();

Thread t2 = new Thread( new Runnable(){
    public void run(){
        // 加載資源2
        資源加載代碼……
        // 本資源加載完後,閉鎖-1
        latch.countDown();
    }
} ).start();

Thread t3 = new Thread( new Runnable(){
    public void run(){
        // 本線程必須等待所有資源加載完後才能執行
        latch.await();
        // 當閉鎖數量為0時,await傳回,執行接下來的任務
        任務代碼……
    }
} ).start();           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2. 同步屏障:CyclicBarrier

2.1 使用場景

若有多條線程,他們到達屏障時将會被阻塞,隻有當所有線程都到達屏障時才能打開屏障,所有線程同時執行,若有這樣的需求可以使用同步屏障。此外,當屏障打開的同時還能指定執行的任務。

2.2 閉鎖 與 同步屏障 的差別

  • 閉鎖隻會阻塞一條線程,目的是為了讓該條任務線程滿足條件後執行;
  • 而同步屏障會阻塞所有線程,目的是為了讓所有線程同時執行(實際上并不會同時執行,而是盡量把線程啟動的時間間隔降為最少)。

2.3 代碼實作

// 建立同步屏障對象,并制定需要等待的線程個數 和 打開屏障時需要執行的任務
CyclicBarrier barrier = new CyclicBarrier(3,new Runnable(){
    public void run(){
        //當所有線程準備完畢後觸發此任務
    }
});

// 啟動三條線程
for( int i=0; i<3; i++ ){
    new Thread( new Runnable(){
        public void run(){
            // 等待,(每執行一次barrier.await,同步屏障數量-1,直到為0時,打開屏障)
            barrier.await();
            // 任務
            任務代碼……
        }
    } ).start();
}           

3. 信号量:Semaphore

3.1 使用場景

若有m個資源,但有n條線程(n>m),是以同一時刻隻能允許m條線程通路資源,此時可以使用Semaphore控制通路該資源的線程數量。

3.2 代碼實作

// 建立信号量對象,并給予3個資源
Semaphore semaphore = new Semaphore(3);

// 開啟10條線程
for ( int i=0; i<10; i++ ) {
    new Thread( new Runnbale(){
        public void run(){
            // 擷取資源,若此時資源被用光,則阻塞,直到有線程歸還資源
            semaphore.acquire();
            // 任務代碼
            ……
            // 釋放資源
            semaphore.release();
        }
    } ).start();
}