使用有什么好处
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情况下,锁存器不能被重用,更重要的是你需要事先知道将创建多少线程(或等到它们在创建锁存器之前全部启动.)
那么在什么情况下闩锁更可取?
CountDown锁存器经常用于与示例完全相反的方式.通常,你会在"await()"上阻塞许多线程,当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
会使你的意图更清晰.
简短的摘要:
Semaphore和CountDownLatch有不同的用途.
使用Semaphore控制对资源的线程访问.
使用CountDownLatch等待所有线程的完成
来自javadocs的信号量定义:
一个信号灯维护一组允许的.如果需要,每个acquire()都会阻止,直到有许可证可用,然后接受它.每个release()都会添加一个许可证,可能会释放一个阻塞收单器.
但是,没有使用实际的许可对象; 该信号量只是不断数量的计数可用,采取相应的行动.
它是如何工作的 ?
信号量用于控制使用资源的并发线程数.该资源可以是共享数据,代码块(临界区)或任何文件.
信号量的计数可以随着不同的线程调用acquire
()和release
()而上下移动.但是在任何时候,你都不能拥有比信号量更大的线程数.
信号量用例:
限制对磁盘的并发访问(由于竞争磁盘搜索,这可能会导致性能下降)
线程创建限制
JDBC连接池/限制
网络连接限制
限制CPU或内存密集型任务
看一下这篇文章的信号量用途.
来自javadocs的CountDownLatch定义:
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助.
它是如何工作的?
CountDownLatch的工作原理是使用一个线程数进行初始化的计数器,每次线程完成执行时它都会递减.当count达到零时,表示所有线程都已完成执行,并且等待锁存器的线程恢复执行.
CountDownLatch用例:
实现最大并行性:有时我们希望同时启动多个线程以实现最大并行度
在开始执行之前等待N个线程完成
死锁检测.
看看这篇文章,清楚地了解CountDownLatch概念.
在这篇文章中也看看Fork Join Pool.它与CountDownLatch有一些相似之处.
假设您走进一家高尔夫专业商店,希望找到一个四人行,
当您排队从一位专业商店服务员proshopVendorSemaphore.acquire()
那里获取开球时间时,您实际上打了个电话,一旦获得开球时间,便打了电话proshopVendorSemaphore.release()
。注意:任何免费服务员都可以为您服务,即共享资源。
现在,您走到入门者那里,他开始了a CountDownLatch(4)
并呼叫await()
等待其他人,对于您来说您称为签入的ie CountDownLatch
。countDown()
其他四人组也是如此。当所有到达时,启动器继续进行(await()
调用返回)
现在,假设每个人都休息了9个洞之后,假设又让初学者参与进来,他使用“ new” CountDownLatch(4)
开出10号洞,等待/同步与1号洞相同。
但是,如果入门者使用a CyclicBarrier
开头,那么他可以在10号孔中重置相同的实例,而不是第二个使用&throw的闩锁。