其他实用程序不太常见,因此有时它们可能会使我们逃脱,但是请记住它们是很好的。 CountDownLatch是这些工具之一。
CountDownLatch –更通用的等待/通知机制
各种Java开发人员都应该熟悉等待/通知方法,直到达到条件为止。 以下是有关其工作原理的一些示例:
public void testWaitNotify() throws Exception {final Object mutex = new Object();Thread t = new Thread() {public void run() {// we must acquire the lock before waiting to be notifiedsynchronized(mutex) {System.out.println("Going to wait " +"(lock held by " + Thread.currentThread().getName() + ")");try {mutex.wait(); // this will release the lock to be notified (optional timeout can be supplied)} catch (InterruptedException e) {e.printStackTrace();} System.out.println("Done waiting " +"(lock held by " + Thread.currentThread().getName() + ")");}}};t.start(); // start her up and let her wait()// not normally how we do things, but good enough for demonstration purposesThread.sleep(1000);// we acquire the lock released by wait(), and notify()synchronized (mutex) {System.out.println("Going to notify " +"(lock held by " + Thread.currentThread().getName() + ")");mutex.notify();System.out.println("Done notify " +"(lock held by " + Thread.currentThread().getName() + ")");}}
输出量
Going to wait (lock held by Thread-0)
Going to notify (lock held by main)
Done notify (lock held by main)
Done waiting (lock held by Thread-0)
实际上, CountDownLatch可以类似于等待/通知,仅使用一个通知即可使用-也就是说,只要您不希望在获取锁并调用wait()之前调用notify()时, wait()就会停顿。 。 因此,它实际上是更宽容的,在某些情况下,这正是您想要的。 这是一个示例:
public void testWaitNotify() throws Exception {final CountDownLatch latch = new CountDownLatch(1); // just one timeThread t = new Thread() {public void run() {// no lock to acquire!System.out.println("Going to count down...");latch.countDown();}};t.start(); // start her up and let her wait()System.out.println("Going to await...");latch.await();System.out.println("Done waiting!");
}
如您所见,它比等待/通知更简单,并且所需的代码更少。 它还允许我们在调用wait()之前调用最终释放该块的条件。 这可能意味着代码更安全。
真实的例子
因此我们知道我们可以将其用作更简单的等待/通知机制,但是您可能已经在上面看到了构造函数参数。 在构造函数中,指定解锁之前需要递减锁存器的次数。 有什么可能的用途? 好吧,它可以使进程等待直到采取了一定数量的动作。
例如,如果您具有可以通过侦听器或类似方法挂接到的异步进程,则可以创建单元测试以验证是否进行了一定数量的调用。 这使我们只需要在正常情况下需要的时间(或在保释并假设失败之前的某个限制)即可。
最近,我遇到了一种情况,我必须验证是否已将JMS消息从队列中拉出并正确处理。 这自然是异步的,并且不在我的控制范围之内,并且也不选择模拟,因为它是具有Spring上下文的完全组装的应用程序,等等。为了测试这一点,我对使用服务进行了微小的更改,以允许在邮件已处理。 然后,我可以临时添加一个侦听器,该侦听器使用CountDownLatch保持测试尽可能接近同步。
这是显示概念的示例:
public void testSomeProcessing() throws Exception {// should be called twicefinal CountDownLatch testLatch = new CountDownLatch(2);ExecutorService executor = Executors.newFixedThreadPool(1);AsyncProcessor processor = new AsyncProcessor(new Observer() {// this observer would be the analogue for a listener in your async processpublic void update(Observable o, Object arg) {System.out.println("Counting down...");testLatch.countDown();}});//submit two tasks to be process// (in my real world example, these were JMS messages)executor.submit(processor);executor.submit(processor);System.out.println("Submitted tasks. Time to wait...");long time = System.currentTimeMillis();testLatch.await(5000, TimeUnit.MILLISECONDS); // bail after a reasonable timelong totalTime = System.currentTimeMillis() - time;System.out.println("I awaited for " + totalTime +"ms. Did latch count down? " + (testLatch.getCount() == 0));executor.shutdown();
}// just a process that takes a random amount of time
// (up to 2 seconds) and calls its listener
public class AsyncProcessor implements Callable<Object> {private Observer listener;private AsyncProcessor(Observer listener) {this.listener = listener;}public Object call() throws Exception {// some processing here which can take all kinds of time...int sleepTime = new Random().nextInt(2000);System.out.println("Sleeping for " + sleepTime + "ms");Thread.sleep(sleepTime);listener.update(null, null); // not standard usage, but good for a demoreturn null;}
}
输出量
Submitted tasks. Time to wait...
Sleeping for 739ms
Counting down...
Sleeping for 1742ms
Counting down...
I awaited for 2481ms. Did latch count down? true
结论
CountDownLatch就是这样。 它不是一个复杂的主题,而且用途有限,但是当您遇到类似我的问题时,很高兴看到示例并知道它们在您的工具箱中。 将来,如果没有其他问题,我一定会牢记这一点,以便进行更简单的等待/通知。 如果您对此帖子或系列中的其他帖子有疑问或评论,请留言。
参考:来自Carfey Software博客的 JCG合作伙伴的Java并发第6部分– CountDownLatch 。
- Java并发教程–信号量
- Java并发教程–重入锁
- Java并发教程–线程池
- Java并发教程–可调用,将来
- Java并发教程–阻塞队列
- Exchanger和无GC的Java
- Java Fork / Join进行并行编程
- 使用迭代器时如何避免ConcurrentModificationException
- 改善Java应用程序性能的快速技巧
翻译自: https://www.javacodegeeks.com/2011/09/java-concurrency-tutorial.html