簡體   English   中英

CountDownLatch 與信號量

[英]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會使您的意圖更加清晰。

簡短的摘要:

  1. SemaphoreCountDownLatch用於不同的目的。

  2. 使用Semaphore來控制線程對資源的訪問。

  3. 使用CountDownLatch等待所有線程完成

Javadocs 中的Semaphore定義:

Semaphore維護一組許可。 如有必要,每個acquire()阻塞,直到獲得許可為止,然后獲取它。 每個release()添加一個許可,可能會釋放一個阻塞的獲取者。

但是,沒有使用實際的許可對象; Semaphore只是計算可用的數量並相應地采取行動。

它是如何工作的?

信號量用於控制使用資源的並發線程數。資源可以是共享數據、代碼塊(臨界區)或任何文件。

當不同的線程調用acquire()release()時, Semaphore的計數可以上下波動。 但是在任何時候,線程數都不能超過信號量計數。

Semaphore用例:

  1. 限制對磁盤的並發訪問(由於競爭磁盤尋道導致性能下降)
  2. 線程創建限制
  3. JDBC 連接池/限制
  4. 網絡連接限制
  5. 限制 CPU 或內存密集型任務

看看這篇文章,了解信號量的使用。

來自 Javadocs 的CountDownLatch定義:

一種同步輔助,允許一個或多個線程等待,直到在其他線程中執行的一組操作完成。

它是如何工作的?

CountDownLatch工作原理是使用線程數初始化計數器,每次線程完成執行時該計數器都會遞減。 當計數為零時,表示所有線程都已完成執行,等待閂鎖的線程繼續執行。

CountDownLatch用例:

  1. Achieving Maximum Parallelism:有時我們想同時啟動多個線程來實現最大並行度
  2. 在開始執行之前等待 N 個線程完成
  3. 死鎖檢測。

看看這篇文章可以清楚地理解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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM