CyclicBarrier、CountDownLatch、Semaphore 的用法
CountDownLatch(線程計數器 )
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("繼續執行主線程");
CyclicBarrier(回環柵欄-等待至 barrier 狀態再全部同時執行)
字面意思回環柵欄,通過它可以實現讓一組線程等待至某個狀態之后再全部同時執行。叫做回環是因為當所有等待線程都被釋放以后,CyclicBarrier 可以被重用。我們暫且把這個狀態就叫做barrier,當調用 await()方法之后,線程就處于 barrier 了。
CyclicBarrier 中最重要的方法就是 await 方法,它有 2 個重載版本:
-
public int await():用來掛起當前線程,直至所有線程都到達 barrier 狀態再同時執行后續任務;
-
public int await(long timeout, TimeUnit unit):讓這些線程等待至一定的時間,如果還有線程沒有到達 barrier 狀態就直接讓到達 barrier 的線程執行后續任務。
具體使用如下,另外 CyclicBarrier 是可以重用的。
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;}@Overridepublic 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("所有線程寫入完畢,繼續處理其他任務,比如數據操作");}}
Semaphore(信號量-控制同時訪問的線程個數)
Semaphore 翻譯成字面意思為 信號量,Semaphore 可以控制同時訪問的線程個數,通過acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。
Semaphore 類中比較重要的幾個方法:
-
public void acquire(): 用來獲取一個許可,若無許可能夠獲得,則會一直等待,直到獲得許可。
-
public void acquire(int permits):獲取 permits 個許可
-
public void release() { } :釋放許可。注意,在釋放許可之前,必須先獲獲得許可。
-
public void release(int permits) { }:釋放 permits 個許可
上面 4 個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法
-
public boolean tryAcquire():嘗試獲取一個許可,若獲取成功,則立即返回 true,若獲取失敗,則立即返回 false
-
public boolean tryAcquire(long timeout, TimeUnit unit):嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回 true,否則則立即返回 false
-
public boolean tryAcquire(int permits):嘗試獲取 permits 個許可,若獲取成功,則立即返回 true,若獲取失敗,則立即返回 false
-
public boolean tryAcquire(int permits, long timeout, TimeUnit unit): 嘗試獲取 permits個許可,若在指定的時間內獲取成功,則立即返回 true,否則則立即返回 false
-
還可以通過 availablePermits()方法得到可用的許可數目。
例子:若一個工廠有 5 臺機器,但是有 8 個工人,一臺機器同時只能被一個工人使用,只有使用完了,其他工人才能繼續使用。那么我們就可以通過 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;}@Overridepublic 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();}}
}
? CountDownLatch 和 CyclicBarrier 都能夠實現線程之間的等待,只不過它們側重點不同;CountDownLatch 一般用于某個線程 A 等待若干個其他線程執行完任務之后,它才執行;而 CyclicBarrier 一般用于一組線程互相等待至某個狀態,然后這一組線程再同時執行;另外,CountDownLatch 是不能夠重用的,而 CyclicBarrier 是可以重用的。? Semaphore 其實和鎖有點類似,它一般用于控制對某組資源的訪問權限。