CountDownLatch CyclicBarrier和 Semaphore
CountDownLatch CyclicBarrier和 Semaphore
原理
- 基於AQS實現。
- 讓需要的暫時阻塞的線程,進入一個死循環裏面,得到某個條件後再退出循環,以此實現阻塞當前線程的效果。
CountDownLatch
- CountDownLatch一般用於某個線程A等待若幹個其他線程執行完任務之後,它才執行;
- CountDownLatch是不能夠重用的
- 利用它可以實現類似計數器的功能。比如有一個任務A,它要等待其他4個任務執行完畢之後才能執行,此時就可以利用CountDownLatch來實現這種功能了。
CountDownLatch類只提供了一個構造器:
public CountDownLatch(int count) { }; //參數count為計數值
然後下面這3個方法是CountDownLatch類中最重要的方法:
public void await() throws InterruptedException { }; //調用await()方法的線程會被掛起,它會等待直到count值為0才繼續執行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()類似,只不過等待一定的時間後count值還沒變為0的話就會繼續執行
public void countDown() { }; //將count值減1
public class Test { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(2); new Thread(){ public void run() { try { System.out.println("子線程"+Thread.currentThread().getName()+"正在執行"); Thread.sleep(3000); System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread(){ public void run() { try { System.out.println("子線程"+Thread.currentThread().getName()+"正在執行"); Thread.sleep(3000); System.out.println("子線程"+Thread.currentThread().getName()+"執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2個子線程執行完畢..."); latch.await(); System.out.println("2個子線程已經執行完畢"); System.out.println("繼續執行主線程"); } catch (InterruptedException e) { e.printStackTrace(); } } } //執行結果 線程Thread-0正在執行 線程Thread-1正在執行 等待2個子線程執行完畢... 線程Thread-0執行完畢 線程Thread-1執行完畢 2個子線程已經執行完畢 繼續執行主線程
CyclicBarrier
- 而CyclicBarrier一般用於一組線程互相等待至某個狀態,然後這一組線程再同時執行;
- 而CyclicBarrier是可以重用的
CyclicBarrier提供2個構造器:
public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
參數parties指讓多少個線程或者任務等待至barrier狀態;參數barrierAction為當這些線程都達到barrier狀態時會執行的內容。然後CyclicBarrier中最重要的方法就是await方法,它有2個重載版本:
public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
第一個版本比較常用,用來掛起當前線程,直至所有線程都到達barrier狀態再同時執行後續任務;
第二個版本是讓這些線程等待至一定的時間,如果還有線程沒有到達barrier狀態就直接讓到達barrier的線程執行後續任務。假若有若幹個線程都要進行寫數據操作,並且只有所有線程都完成寫數據操作之後,這些線程才能繼續做後面的事情,此時就可以利用CyclicBarrier了:
public class Test {
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;
}
@Override
public void run() {
System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據...");
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("所有線程寫入完畢,繼續處理其他任務...");
}
}
}
//執行結果
線程Thread-0正在寫入數據...
線程Thread-3正在寫入數據...
線程Thread-2正在寫入數據...
線程Thread-1正在寫入數據...
線程Thread-2寫入數據完畢,等待其他線程寫入完畢
線程Thread-0寫入數據完畢,等待其他線程寫入完畢
線程Thread-3寫入數據完畢,等待其他線程寫入完畢
線程Thread-1寫入數據完畢,等待其他線程寫入完畢
所有線程寫入完畢,繼續處理其他任務...
所有線程寫入完畢,繼續處理其他任務...
所有線程寫入完畢,繼續處理其他任務...
所有線程寫入完畢,繼續處理其他任務...
Semaphore
- Semaphore其實和鎖有點類似,它一般用於控制對某組資源的訪問權限。
- Semaphore翻譯成字面意思為 信號量,Semaphore可以控同時訪問的線程個數,通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。
- 假若一個工廠有5臺機器,但是有8個工人,一臺機器同時只能被一個工人使用,只有使用完了,其他工人才能繼續使用。那麽我們就可以通過Semaphore來實現:
public class Test {
public static void main(String[] args) {
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;
}
@Override
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();
}
}
}
}
CountDownLatch CyclicBarrier和 Semaphore