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

那么在什么情况下,闩锁可能更可取?

CountDown锁存器经常用于和你的例子完全相反的地方。 通常,在“await()”上会有许multithreading阻塞,当countown达到零时,这些线程都会同时启动。

 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闩锁可以安全地用在你的例子中显示的方式。

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

信号量用于控制使用资源的并发线程的数量。 该资源可以像文件一样,也可以通过限制执行的线程数量成为cpu。 信号量的计数可以随着不同的线程调用acquire()release()而升降。

在你的例子中,你基本上使用Semaphore作为一种Count Up Latch。 鉴于你的意图是等待所有的线程完成,使用CountdownLatch使你的意图更清晰。

简短的摘要:

  1. 信号量和CountDownLatch服务于不同的目的。

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

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

来自javadocs的信号量定义:

信号量拥有一套许可证。 如果需要,每个获取()都会阻塞,直到获得许可证为止。 每个版本()都添加一个许可证,可能会释放一个阻止的获取者。

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

它是如何工作的 ?

信号量用于控制正在使用资源的并发线程数。该资源可以是共享数据或代码块( 临界区 )或任何文件。

信号量的计数可以随着不同的线程调用acquire ()和release ()而升降。 但是在任何时候,线程的数量都不能超过信号量。

信号量用例:

  1. 限制对磁盘的并发访问(由于竞争性的磁盘search,这可能会导致性能下降)
  2. 线程创build限制
  3. JDBC连接池/限制
  4. networking连接限制
  5. 限制CPU或内存密集型任务

看看这篇文章中的信号量使用。

来自javadocs的CountDownLatch定义:

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

它是如何工作的?

CountDownLatch通过使用一个线程数来初始化一个计数器来工作,每当一个线程完成其执行时递减。 当计数达到零时,表示所有的线程都已经完成执行,线程等待锁存器恢复执行。

CountDownLatch用例:

  1. 实现最大的并行性:有时我们想要同时启动多个线程来实现最大的并行性
  2. 在开始执行之前等待N个线程完成
  3. 死锁检测。

看看这篇文章 ,明白了解CountDownLatch的概念。

在这篇文章中也可以看看Fork Join Pool 。 它与CountDownLatch有一些相似之处。

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

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

现在你走起步,他启动一个CountDownLatch(4)并调用await()等待其他人,你的部分,你所谓的签入,即CountDownLatchcountDown()和其余的四人组。 当所有到达时,启动器继续( await()调用返回)

现在,在九洞之后,你们每个人都rest一下,假设让他再次涉及起跑者,他使用一个新的CountDownLatch(4)开球10洞,和洞1一样的等待/同步。

但是,如果启动器使用CyclicBarrier开始,他可以重置10孔中的相同实例,而不是使用&throw的第二个锁存器。

从可用的来源来看,这两个课程的实施没有什么神奇的,所以它们的performance应该差不多。 select让你的意图更明显的一个。

CountdownLatch使线程在await()方法上等待,直到count达到零的时间。 所以也许你想让所有的线程等待3个调用的东西,然后所有的线程都可以继续。 锁存器通常不能被重置。

信号量允许线程检索许可证,这可以防止太多的线程立即执行,如果它不能获得它需要的许可证,则阻塞线程。 许可证可以返回到信号量,允许其他等待线程继续。