CountDownLatch与Semaphore

时间:2008-10-08 18:25:21

标签: java multithreading concurrency semaphore countdownlatch

使用

有什么好处

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:CountDownLatch

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情况下,锁存器不能被重用,更重要的是你需要事先知道将创建多少线程(或者等到它们在创建锁存器之前全部启动。)

那么在什么情况下闩锁会更好?

6 个答案:

答案 0 :(得分:101)

CountDown锁存器经常用于与示例完全相反的方法。通常,你会在“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();
}

总而言之,CountDown锁存器可以安全地以您在示例中显示的方式使用。

答案 1 :(得分:62)

CountDownLatch用于启动一系列线程,然后等待所有线程完成(或直到他们调用countDown()一定次数。

信号量用于控制使用资源的并发线程数。该资源可以类似于文件,也可以通过限制执行的线程数来成为cpu。信号量的计数可以随着不同的线程调用acquire()release()而上下移动。

在您的示例中,您实际上将Semaphore用作一种Count UP Latch。鉴于您的意图是等待所有线程完成,使用CountdownLatch会使您的意图更加清晰。

答案 2 :(得分:14)

简短摘要:

  1. SemaphoreCountDownLatch有不同的用途。

  2. 使用信号量来控制对资源的线程访问。

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

  4. 来自javadocs的

    信号量定义:

      

    信号量维护一组许可。如有必要,每个 acquire()阻止,直到许可可用,然后接受。每个 release()都会添加一个许可证,可能会释放阻塞收单器。

    但是,没有使用实际的许可对象; 信号量只保留可用数量的计数并相应地采取行动。

    它是如何运作的?

    信号量用于控制使用资源的并发线程数。该资源可以是共享数据,代码块(关键部分)或任何文件。

    当不同的线程调用acquire()和release()时,信号量的计数可以上下变化。但是在任何时候,你都不能拥有比信号量更大的线程数。

    信号量用例:

    1. 限制对磁盘的并发访问(由于这会导致性能下降) 竞争盘寻求)
    2. 线程创建限制
    3. JDBC连接池/限制
    4. 网络连接限制
    5. 限制CPU或内存密集型任务
    6. 请查看此article信号量用途。

      来自javadocs的

      CountDownLatch 定义:

        

      允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。

      它是如何运作的?

      CountDownLatch 通过使用线程数初始化计数器来工作,每次线程完成执行时,该计数器都会递减。当count达到零时,表示所有线程都已完成执行,并且线程等待锁存器恢复执行。

      CountDownLatch用例:

      1. 实现最大并行度:有时我们想要启动一些 线程同时实现最大并行性
      2. 等待N个线程在开始执行前完成
      3. 死锁检测。
      4. 查看此article以清楚地了解CountDownLatch概念。

        也可以在此Fork Join Pool查看article。它与 CountDownLatch 有一些相似之处。

答案 3 :(得分:4)

说你走进高尔夫专卖店,希望找到一个四人组,

当你排队等待专业店员的开球时间时,基本上你打电话给proshopVendorSemaphore.acquire(),一旦你开球,你就打电话给proshopVendorSemaphore.release()。注意:任何一个免费服务员可以为您服务,即共享资源。

现在你走到首发位置,他开始CountDownLatch(4)并致电await()等待其他人,就你所谓的CountDownLatch countDown()而言。await()其他四人组也是如此。当所有人到达时,启动器会继续(CountDownLatch(4)呼叫返回)

现在,当你们每个人休息九洞之后,假设再次涉及首发,他使用“新”CyclicBarrier开球10洞,与洞1相同的等待/同步。

但是,如果启动器开始使用{{1}},他可以在第10洞重置相同的实例而不是第二个锁存器,它使用&amp;抛出。

答案 4 :(得分:1)

查看免费提供的源代码,两个类的实现没有任何魔力,因此它们的性能应该大致相同。选择一个让你的意图更明显的那个。

答案 5 :(得分:0)

CountdownLatch使线程在await()方法上等待,直到计数达到零为止。所以也许你希望所有的线程都等到3次调用某个东西,然后所有的线程都可以去。 Latch通常无法重置。

信号量允许线程检索许可,这可以防止过多的线程一次执行,如果无法获得继续执行所需的许可,则阻塞。许可证可以返回到信号量,允许其他等待线程继续进行。