CountDownLatch
在 java 中是一个高级同步实用程序,用于防止特定线程开始处理,直到所有线程都准备好。
但是,Semaphore
完全可以做同样的事情。那么,有什么优点CountDownLatch
呢?
还有一个问题:如果CountDownLatch
确实有一些优点,为什么它被设计为只使用一次?我认为添加 set 方法来重置计数很容易。
CountDownLatch
在 java 中是一个高级同步实用程序,用于防止特定线程开始处理,直到所有线程都准备好。
但是,Semaphore
完全可以做同样的事情。那么,有什么优点CountDownLatch
呢?
还有一个问题:如果CountDownLatch
确实有一些优点,为什么它被设计为只使用一次?我认为添加 set 方法来重置计数很容易。
从语义上讲,它们是不同的。这很重要,因为它使您的代码更易于阅读。当我看到 Semaphore 时,我立即开始思考“有限数量的共享资源”。当我看到一个 CountDownLatch 时,我立即开始思考“一堆线程在等待'开始!' 信号。” 如果你在实际需要后者的代码中给我前者,那就令人困惑了。
从这个意义上说,将 Semaphore 用作 CountDownLatch 有点像花园小径句;虽然在技术上是正确的,但它会导致人们误入歧途并使他们感到困惑。
就更实用的用途而言,如果您只需要 CountDownLatch 就更简单了。越简单越好!
至于重用 CountDownLatch,这会使它的使用复杂化。例如,假设您正在尝试将线程 A、B 和 C 排队以完成某些工作。您让它们在闩锁上等待,然后释放它。然后你重置它,大概是为了让线程 D、E 和 F 排队进行其他工作。但是如果(由于竞态条件)线程 B 还没有真正从第一个锁存器中释放,会发生什么?如果它甚至还没有接到await()
电话怎么办?你是否关闭了它的门,并告诉它与 D、E 和 F 一起等待第二次打开?如果第二次打开取决于 B 应该做的工作,那甚至可能导致僵局!
当我第一次阅读有关 CountDownLatch 的信息时,我对重置有同样的问题。但在实践中,我什至很少想要重置一个。“wait then go”(ABC,然后是 DEF)的每个单元自然适合创建自己的 CountDownLatch 以配合它,并且事情保持良好和简单。
当达到 0时,具有 n 个块的信号量。
n 个块的CountDownLatch直到达到 0。然后几乎在同一时间继续进行。
因此,信号量就像迪斯科舞厅的看门人,倒计时闩锁就像游戏场的开场球。
信号量完全可以做同样的事情。那么,CountDownLatch 的意义何在?
Semaphore
维护一组许可证。如有必要,每个人都会acquire()
阻止,直到获得许可,然后再接受。每个都release()
添加一个许可证,可能会释放一个阻塞的收购方。
但是,没有使用实际的许可对象;Semaphore 只是对可用数量进行计数并采取相应措施。
Semaphore
正在阻塞进入临界区的入口,并且CountDownLatch
正在阻塞主线程的执行,除非其他线程完成它们的任务。
查看此 SE 问题以了解更多详细信息:
如果 CountDownLatch 确实有一些要点,为什么它被设计为只使用一次?
如果要重复使用,请使用CyclicBarrier
一种同步辅助工具,它允许一组线程相互等待以达到共同的障碍点。CyclicBarriers 在涉及固定大小的线程组的程序中很有用,这些线程组必须偶尔相互等待。屏障被称为循环的,因为它可以在等待线程被释放后重新使用。
CountDownLatch 的 Javadocs 引用:
CountDownLatch 是一次性现象——计数无法重置。如果您需要重置计数的版本,请考虑使用 CyclicBarrier。