[英]CountDownLatch vs. Semaphore
使用有什么好處嗎
java.util.concurrent.CountdownLatch
代替
java.util.concurrent.Semaphore ?
據我所知,以下片段幾乎是等效的:
1. 信號量
final Semaphore sem = new Semaphore(0);
for (int i = 0; i < num_threads; ++ i)
{
Thread t = new Thread() {
public void run()
{
try
{
doStuff();
}
finally
{
sem.release();
}
}
};
t.start();
}
sem.acquire(num_threads);
2:倒計時鎖存器
final CountDownLatch latch = new CountDownLatch(num_threads);
for (int i = 0; i < num_threads; ++ i)
{
Thread t = new Thread() {
public void run()
{
try
{
doStuff();
}
finally
{
latch.countDown();
}
}
};
t.start();
}
latch.await();
除了在 #2 的情況下,閂鎖不能被重用,更重要的是,您需要提前知道將創建多少線程(或等到它們全部啟動后再創建閂鎖。)
那么在什么情況下閂鎖更可取呢?
CountDownLatch
經常用於與您的示例完全相反的情況。 通常,您會在await()
上阻塞許多線程,當計數達到零時,它們都會同時啟動。
final CountDownLatch countdown = new CountDownLatch(1);
for (int i = 0; i < 10; ++ i) {
Thread racecar = new Thread() {
public void run() {
countdown.await(); //all threads waiting
System.out.println("Vroom!");
}
};
racecar.start();
}
System.out.println("Go");
countdown.countDown(); //all threads start now!
您還可以將其用作 MPI 樣式的“屏障”,使所有線程在繼續之前等待其他線程趕上某個點。
final CountDownLatch countdown = new CountDownLatch(num_thread);
for (int i = 0; i < num_thread; ++ i) {
Thread t= new Thread() {
public void run() {
doSomething();
countdown.countDown();
System.out.printf("Waiting on %d other threads.",countdown.getCount());
countdown.await(); //waits until everyone reaches this point
finish();
}
};
t.start();
}
盡管如此, CountDownLatch
可以按照您在示例中顯示的方式安全地使用。
CountDownLatch用於啟動一系列線程,然后等待所有線程完成(或直到它們調用countDown()
給定次數。
信號量用於控制使用資源的並發線程數。 該資源可以是文件之類的東西,也可以是限制執行線程數的 cpu。 當不同的線程調用acquire()
和release()
時,信號量的計數可以上下波動。
在您的示例中,您本質上是將信號量用作一種 Count UP Latch。 鑒於您的意圖是等待所有線程完成,使用CountdownLatch
會使您的意圖更加清晰。
簡短的摘要:
Semaphore
和CountDownLatch
用於不同的目的。
使用Semaphore
來控制線程對資源的訪問。
使用CountDownLatch
等待所有線程完成
Javadocs 中的Semaphore
定義:
Semaphore
維護一組許可。 如有必要,每個acquire()
阻塞,直到獲得許可為止,然后獲取它。 每個release()
添加一個許可,可能會釋放一個阻塞的獲取者。
但是,沒有使用實際的許可對象; Semaphore
只是計算可用的數量並相應地采取行動。
它是如何工作的?
信號量用於控制使用資源的並發線程數。資源可以是共享數據、代碼塊(臨界區)或任何文件。
當不同的線程調用acquire()
和release()
時, Semaphore
的計數可以上下波動。 但是在任何時候,線程數都不能超過信號量計數。
Semaphore
用例:
看看這篇文章,了解信號量的使用。
來自 Javadocs 的CountDownLatch
定義:
一種同步輔助,允許一個或多個線程等待,直到在其他線程中執行的一組操作完成。
它是如何工作的?
CountDownLatch
工作原理是使用線程數初始化計數器,每次線程完成執行時該計數器都會遞減。 當計數為零時,表示所有線程都已完成執行,等待閂鎖的線程繼續執行。
CountDownLatch
用例:
看看這篇文章可以清楚地理解CountDownLatch
概念。
也可以在這篇文章中查看Fork Join Pool 。 它與CountDownLatch
有一些相似之處。
假設你走進高爾夫專賣店,希望找到一個四人組,
當您排隊從一位專業商店服務員那里獲得開球時間時,本質上您調用了proshopVendorSemaphore.acquire()
,一旦您獲得開球時間,您就會調用proshopVendorSemaphore.release()
。注意:任何免費服務員都可以為您服務,即共享資源。
現在你走到 starter,他啟動一個CountDownLatch(4)
並調用await()
等待其他人,對於你來說,你調用了 check-in ,即CountDownLatch
。 countDown()
等四人組的其余部分也是如此。 全部到達后,starter 繼續執行( await()
調用返回)
現在,在九個洞之后,當你們每個人都休息時,假設讓首發球員再次參與,他使用“新的” CountDownLatch(4)
在第 10 洞開球,與第 1 洞相同的等待/同步。
但是,如果起始者使用CyclicBarrier
開始,他可以在第 10 洞重置相同的實例,而不是使用 & throw 的第二個閂鎖。
查看免費提供的源代碼,這兩個類的實現沒有什么神奇之處,因此它們的性能應該大同小異。 選擇使您的意圖更加明顯的一種。
CountdownLatch
使線程等待await()
方法,直到計數達到零。 因此,也許您希望所有線程都等待 3 次調用某事,然后所有線程都可以運行。 A Latch
一般不能復位。
Semaphore
允許線程檢索許可,這可以防止過多的線程同時執行,如果無法獲得繼續執行所需的許可就會阻塞。 許可可以返回給Semaphore
允許其他等待的線程繼續進行。
信號量通過使用計數器來控制對共享資源的訪問。 如果計數器大於零,則允許訪問。 如果為零,則拒絕訪問。 計數器正在計算允許訪問共享資源的許可。 因此,要訪問資源,必須從信號量中授予線程許可。
CountDownlatch 使線程等待一個或多個事件發生。 countDownLatch 最初是用在閂鎖釋放之前發生的事件數量的計數創建的。 每次發生事件時,計數都會遞減。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.