CountDownLatch
是什么
CountDownLatch的字面意思:倒計時 門栓
它的功能是:讓一些線程阻塞直到另一些線程完成一系列操作后才喚醒。
它通過調用await方法讓線程進入阻塞狀態等待倒計時0時喚醒。
它通過線程調用countDown方法讓倒計時中的計數器減去1,當計數器為0時,會喚醒哪些因為調用了await而阻塞的線程。
- 底層是使用AQS實現的
案例
假設老板開一個緊急會議,他先到會議室等着所有人簽到然后開始開會,可以使用CountDownLatch進行模擬。
public static void main(String[] args) {
CountDownLatch countDownLatch=new CountDownLatch(5);
for (int i=1;i<=5;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"簽到!");
countDownLatch.countDown();
},"第"+i+"個人").start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("老板宣布人夠了開始開會!");
}
運行結果:
//注意這里的簽到順序可以隨意
第1個人簽到!
第2個人簽到!
第3個人簽到!
第4個人簽到!
第5個人簽到!
老板宣布人夠了開始開會!
CyclicBarrier
是什么
CyclicBarrier [ˈsaɪklɪk] [ˈbæriər] 的字面意思:可循環使用的屏障 【柵欄】
它的功能是:讓一組線程到達一個屏障時被阻塞,知道最后一個線程到達屏障,所有被屏障攔截的線程才會繼續執行。
它通過調用await方法讓線程進入屏障,
- 底層是通過ReentrantLock以及Condition中的await和signal實現
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
圖例
例子
還是上面的開會的例子,我們使用CyclicBarrier實現。
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(5,()->{
System.out.println("老板宣布人夠了開始開會!!");
});
for (int i=1;i<=5;i++){
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"簽到!");
cyclicBarrier.await(); //如果等待的線程數未到,這里將一直等待
//等線程數夠了下面語句將繼續執行......
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},"第"+i+"個人").start();
}
}
運行結果:
第1個人簽到!
第2個人簽到!
第3個人簽到!
第4個人簽到!
第5個人簽到!
老板宣布人夠了開始開會!!
Semaphor
是什么
Semaphor [ˈseməfɔːr] 信號量的意思;
它主要用於兩個目的:
- 用於多個共享資源互斥使用。【也就是具有鎖的功能】
一個停車場有多個停車位,這多個停車位對應汽車來說就是多個共享資源,Semaphor可以實現多個停車位和多個汽車之間的互斥。 - 用於控制並發線程數。(就是控制同時得到共享資源的線程數量)
在創建Semaphor時可以指定並發線程數,
//permits :允許
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//還可以指定是否為公平鎖
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
- 信號量為1時 相當於獨占鎖 信號量大於1時相當於共享鎖。
例子
我們測試使用Semaphor實現控制線程並發訪問方法。
/**
* 測試使用Semaphor信號量控制方法的訪問
*/
public class TestSemaphor {
Semaphore semaphore=new Semaphore(5);
/**
* 假設並發只能為5個
*/
public void pay(){
try {
//在 semaphore.acquire() 和 semaphore.release()之間的代碼,同一時刻只允許指定個數的線程進入!
semaphore.acquire();
System.out.println("這是支付的方法!");
Thread.sleep(2000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestSemaphor ts=new TestSemaphor();
for (int i=0;i<10;i++){
new Thread(()->{
ts.pay();
},"線程"+i).start();
}
}
}
運行效果:
線程0這是支付的方法!
線程3這是支付的方法!
線程6這是支付的方法!
線程5這是支付的方法!
線程1這是支付的方法!
//注意這里是執行的暫停點
線程7這是支付的方法!
線程8這是支付的方法!
線程2這是支付的方法!
線程4這是支付的方法!
線程9這是支付的方法!
注意結果:10個線程分了兩批執行的,也就是說我們控制了訪問pay方法的線程數量,每次並發只能是5個!
CountDownlatch和CyclicBarrier以及Semaphor的區別是
-
CountDownLatch是做減法,CyclicBarrier是做加法,Semaphor的臨界資源可以反復使用
-
CountDownLatch不能重置計數,CycliBarrier提供的reset()方法可以重置計數,不過只能等到第一個計數結束。Semaphor可以重復使用。
-
CountDownLatch和CycliBarrier不能控制並發線程的數量,Semaphor可以實現控制並發線程的數量。