二元信号量 vs 可重入锁

36
我一直在尝试理解可重入锁和信号量( 可重入锁的嵌套与释放/解锁机制 )。
似乎使用信号量需要编写更经过充分测试的应用程序,因为 release() 方法不会检查释放许可的线程是否实际持有它。当我测试我的测试代码时,我发现这可能会随后增加许可证数量超出初始限制。另一方面,如果一个线程在调用 unlock 方法时没有持有可重入锁,则会出现 IllegalMonitorException。
所以说,有没有真正的理由拥有二进制信号量,因为二进制信号量能做的所有事情都可以通过 ReentrantLock 来完成。如果我们使用二进制信号量,我们将不得不检查整个方法调用堆栈,以查看是否已获取许可证之前( 如果有可能后续获取 - 如果释放不进行处理,可能会阻塞等等 )。此外,由于可重入锁还为每个对象提供一个锁,是否总是更好地选择可重入锁而不是二进制信号量呢?
我查看了这里的一篇帖子,它谈到了二进制信号量和互斥量之间的区别,但是在 Java 中是否有互斥量这样的东西呢?
谢谢, Chan.

附言 - 我在另一个论坛(http://www.coderanch.com/t/615796/threads/java/reason-prefer-binary-Semaphore-Reentrant)发布了这个问题,但还没有收到回复。我想也在这里发布一下,看看能得到什么。


http://stackoverflow.com/questions/12641933/difference-between-semaphore-and-condition-reentrantlock 可能会有所帮助。 - user2579943
2
该行为是按设计的,而且有充分记录:“释放许可的线程不一定要通过调用 acquire() 来获取该许可。在应用程序中,通过编程约定来确立信号量的正确使用方式。”当使用 ReentrantLocks 时,必须确保在 finally 块中正确释放锁。这也没有强制执行,但同样有充分的记录。 - assylias
1
参见:https://dev59.com/iGsz5IYBdhLWcg3w8Mmr - assylias
3个回答

37

从技术上讲,二进制信号量没有任何真正的理由存在,因为 ReentrantLock 可以实现二进制信号量的所有功能。

如果你只需要可重入互斥,那么使用 ReentrantLock 要比二进制信号量更好。但如果需要非拥有者释放语义,则只能选择信号量。

另外,由于可重入锁也为每个对象提供了一个锁,所以是否总是最好使用可重入锁而不是二进制信号量?

这取决于具体需求。如前所述,如果只需要简单的互斥,则不要选择信号量。如果多个线程(但数量有限)可以进入关键部分,可以通过线程封闭或信号量来实现。

我在这里查看了一篇介绍二进制信号量和互斥锁区别的帖子,但 Java 中是否有类似于互斥锁的东西呢?

ReentrantLocksynchronized 都是 Java 中互斥锁的例子。


1
谢谢,约翰。你的回复很快也很有帮助。 - Chan

8

由于John已经对可重入锁做了很好的解释,这里将不再赘述。在Java中,互斥锁的一个例子是Synchronized关键字。

然而,如果出于任何原因,您想要更好地控制锁定机制,Semaphore就会变得非常方便。这意味着,您的代码将需要负责谁调用了acquire()和谁调用了release(),因为Semaphore本质上是盲目的,只关心许可证是否可用。

另一种使用Java实现自己的互斥锁的方法是LockSupport。它类似于Semaphore,但在允许许可证时具有超时,使用park()函数,并且仅支持一个许可证,而不像Semaphore支持多个许可证。


6

Semaphore(信号量)和可重入锁之间存在一些小差异。

  • Semaphore(信号量)可以被另一个线程释放。Semaphore的Javadoc指出,这种行为在某些特殊情况下(如死锁恢复)可能是有用的,因此它应该只在真正需要的特殊情况下使用。
  • 此外,二进制信号量不可重入。您无法在同一线程中第二次获取二进制信号量。这将导致死锁(自我阻塞线程!),您可能需要一些已经提到的死锁恢复机制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接