1. 程式人生 > 實用技巧 >CountDownLatch, CyclicBarrier, Semaphore 和 Phaser

CountDownLatch, CyclicBarrier, Semaphore 和 Phaser

CountDownLatch

計數與阻塞是分離的(相對活),當計數器為0時釋放阻塞執行緒,不可重置,不可複用。

參與計數的執行緒不用阻塞,需要阻塞的執行緒不用參與計數。

主要方法

await:阻塞當前執行緒(可設定超時時間)

countdown:計數減1

getCount:返回當前計數

示例

 1 public class CountDownLatchTest {
 2 
 3     static final CountDownLatch countDownLatch = new CountDownLatch(5);
 4 
 5     public static void main(String[] args) {
6 for (int i = 0; i < 5; i++) { 7 new Thread(()->{ 8 try { 9 Thread.sleep(new Double(Math.random() * 5000).longValue()); 10 System.out.println(Thread.currentThread().getName() + "計數"); 11 countDownLatch.countDown(); //
計數減1 12 System.out.println(Thread.currentThread().getName() + "繼續執行"); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 }).start(); 17 } 18 try { 19 System.out.println("主執行緒進入阻塞");
20 countDownLatch.await(); // 阻塞,計數器為0時釋放 21 System.out.println("主執行緒開始執行"); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 }

執行結果

Cyclic Barrier

計數與阻塞是不分離的(不夠靈活),當計數器達到指定值時釋放,可重置,可複用。

參與計數的一定是阻塞執行緒,需要阻塞的執行緒一定參與計數。

主要方法

過載的構造方法:CyClicBarrier(int parties, Runnable barrierAction)(指定屏障操作)

await:阻塞當前執行緒,並且計數加1(可設定超時時間)

getNumberWaiting:返回當前阻塞的執行緒數目(當前計數)

getParties:返回計數器指定的值

reset:將迴圈屏障重置為初始狀態(計數歸0)

示例

 1 public class CyclicBarrierTest {
 2 
 3     static final CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 5; i++) {
 7             new Thread(()->{
 8                 try {
 9                     Thread.sleep(new Double(Math.random() * 5000).longValue());
10                     System.out.println(Thread.currentThread().getName() + "阻塞並且計數");
11                     cyclicBarrier.await(); // 阻塞並且計數加1
12                     System.out.println(Thread.currentThread().getName() + "繼續執行第一部分");
13                     Thread.sleep(new Double(Math.random() * 5000).longValue());
14                     System.out.println(Thread.currentThread().getName() + "阻塞並且計數");
15                     cyclicBarrier.await(); // 阻塞並且計數加1
16                     System.out.println(Thread.currentThread().getName() + "繼續執行第二部分");
17                 } catch (InterruptedException e) {
18                     e.printStackTrace();
19                 } catch (BrokenBarrierException e) {
20                     e.printStackTrace();
21                 }
22             }).start();
23         }
24     }
25 }

執行結果

Semaphore

類似鎖機制,但本質依然是執行緒的排程,可以控制同時使用該訊號量的執行緒個數。

主要方法

過載的構造方法:Semaphore(int permits, boolean fair)(指定公平性)

acquire:從此訊號量獲取一個許可,獲取到許可之前執行緒將被阻塞

release:釋放當前許可

availablePermits:返回此訊號量的可用許可數目

示例

 1 public class SemaphoreTest {
 2 
 3     static final Semaphore semaphore = new Semaphore(2);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 5; i++) {
 7             new Thread(()->{
 8                 try {
 9                     semaphore.acquire(); // 獲取一個許可
10                     System.out.println(Thread.currentThread().getName() + "開始執行");
11                     Thread.sleep(new Double(Math.random() * 5000).longValue());
12                     System.out.println(Thread.currentThread().getName() + "執行結束");
13                     semaphore.release(); // 釋放當前許可
14                 } catch (InterruptedException e) {
15                     e.printStackTrace();
16                 }
17             }).start();
18         }
19     }
20 }

執行結果

Phaser

結合了CountDown和CyclicBarrier的特性,還具備分階段的功能。

主要方法

arrive:抵達

awaitAdvance:阻塞等待某階段結束

arriveAndAwaitAdvance:抵達並且阻塞等待

register:註冊

arriveAndDeregister:抵達並且登出

getPhase:獲得當前階段數

示例

 1 public class PhaserTest {
 2 
 3     static final Phaser phaser = new Phaser(2);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 3; i++) {
 7             new Thread(()->{
 8                 try {
 9                     Thread.sleep(new Double(Math.random() * 5000).longValue());
10                     if (phaser.getPhase() == 0) {
11                         System.out.println(Thread.currentThread().getName() + "抵達第一階段");
12                         phaser.arriveAndAwaitAdvance();
13                     }
14                     Thread.sleep(new Double(Math.random() * 5000).longValue());
15                     if (phaser.getPhase() <= 1) {
16                         System.out.println(Thread.currentThread().getName() + "抵達第二階段");
17                         phaser.arriveAndAwaitAdvance();
18                     }
19                     Thread.sleep(new Double(Math.random() * 5000).longValue());
20                     if (phaser.getPhase() <= 2) {
21                         System.out.println(Thread.currentThread().getName() + "抵達第三階段");
22                         phaser.arrive();
23                     }
24                 } catch (InterruptedException e) {
25                     e.printStackTrace();
26                 }
27             }).start();
28         }
29         phaser.awaitAdvance(0);
30         System.out.println("第一階段結束");
31         phaser.register(); // 註冊一個
32         phaser.awaitAdvance(1);
33         System.out.println("第二階段結束");
34         phaser.arriveAndDeregister(); // 登出一個
35         phaser.awaitAdvance(2);
36         System.out.println("第三階段結束");
37     }
38 }

執行結果