【问题标题】:Is it better to synchronize with semaphores or with monitors?与信号量同步还是与监视器同步更好?
【发布时间】:2011-07-02 07:31:20
【问题描述】:

与信号量同步还是与监视器同步更好?

【问题讨论】:

  • 您需要同步的具体问题是什么?
  • 一般来说要么比另一个好,同时也更差。

标签: java synchronization monitor


【解决方案1】:

如果您希望最大限度地减少头痛,首选监视器synchronized 块/方法)而不是信号量,只要您觉得是锁定原语的真正选择。 忽略关于信号量灵活性的学术讨论。您追求的是可靠性,而不是可配置性,不是吗?

通常声称监视器和信号量是等价的(可以相互模拟),但这种等价比sometimes expected 要抽象得多,也没有多大用处。 任何能够正确模拟一个与另一个的人都不再需要这个问题的任何答案。

显然,在允许同时进入块的线程数大于 1 的情况下,信号量是您唯一实用的选择。因此,监视器的真正竞争对手是二进制信号量,即初始化计数为 1 的信号量,另外只有那些您希望执行锁定的同一线程最终解锁信号量的那些。因此,让我们仔细看看这些情况。

监视器和二进制信号量的根本区别在于线程所有权。它有一个很大的后果,那就是重入的能力。重入意味着已经拥有锁的线程可以再次获取它,而不是死锁。如果您的类具有共享状态,而您只是想在所有方法中保护这些状态,但又无法承担这些方法如何相互调用的永久假设,这将是一件大事;或具有在您的线程安全方案中发展的任何皮带和大括号特性。

信号量永不重入,Java 监视器总是可重入的。如果您需要从多个代码位置锁定同一个对象,semaphores are prone to deadlocks 即使在单线程场景中 - 信号量的这种限制会带来任何仅在相对罕见的情况下才有好处,无论如何显示器都不是真正的选择。

线程所有权还显着降低了忘记锁定、忘记解锁或一个线程的活动掩盖另一个线程的活动的风险。相关的 Java 语法也存在显着的人体工程学差异。

另请注意this question;尽管它使用了不同的术语,但更好的答案将“互斥锁”理解为 Java“监视器”。

【讨论】:

    【解决方案2】:

    为了确认,监视器是指古老的 synchronized 关键字。

    第一个问题,你的锁需要柜台吗?

    1. 信号量可以有一个大于一的计数器。如果需要保护N个资源,Semaphore是最好的。轻松决定。
    2. 如果您要保护单个资源,这是一个有趣的案例,信号量 (1) 和监视器同样适用。

    J2SE 5.0 concurrency article 在这里给出了很好的建议。监视器受到限制,因为:

    • 无法退出尝试获取已持有的锁,或在等待指定时间段后放弃,或在中断后取消锁定尝试。
    • 无法更改锁的语义,例如,关于可重入性、读写保护或公平性。
    • 同步在方法和块内完成,因此限制了对严格块结构锁定的使用。换句话说,您不能在一种方法中获取锁并在另一种方法中释放它

    因此,如果这些项目中的任何一项对您很重要(超时后退出就是一个很好的例子),那么请使用信号量。如果没有,显示器就可以了。

    【讨论】:

      【解决方案3】:

      首先,您必须确定您使用的是哪个 JDK。 第一个仅提供线程的 Java 版本。自 Java Tiger (5.0) 以来,引入了新的类来处理并发。 特别是,提供了一个完整的包,java.util.concurrent

      根据我的经验,我发现监视器更好,因为它们让代码更干净。此外,使用它们让代码更易于理解。它们通常通过实现 Lock 接口的类来实现:JDK 提供的最著名的实现是定义通用锁的 ReentrantLock 类和 >ReentrantReadWriteLock 类,提供特定的和/或锁。

      因此,锁用于启用/禁用对共享对象(例如对象列表)的访问。

      Semaphore 对象是用于协调和控制线程的同步器(最新的 JDK 中提供了许多同步器,如 SemaphoreCyclicBarrier em>、CountdownLatchExchanger 类)。 例如,使用信号量,您可以向线程池释放固定数量的令牌,从而决定可以同时执行的操作数量。就个人而言,我不喜欢这种方法,因为使用带有 Futures 和 Locks 的线程池会以更清洁和更安全的方式产生相同的结果。

      更多信息可以在本书中找到:“Java Concurrency in Practice”和在 IBM 的教程中:“Concurrency in JDK 5.0”。更多不错的例子可以在here找到。

      【讨论】:

        【解决方案4】:

        “更好”取决于上下文。根据 James McParlane 的说法,它们“同样强大”。我强烈推荐查看his blog for a discussion on the differences

        这是我找到的快速指南:

        信号量

        • 可以在程序的任何地方使用,但不应该在监视器中使用
        • Wait() 并不总是阻塞调用者(即,当信号量计数器大于零时)。
        • Signal() 要么释放一个阻塞线程(如果有的话),要么增加信号量计数器。
        • 如果Signal() 释放阻塞的线程,调用者和释放的线程都会继续。

        条件变量

        • 只能在监视器中使用
        • Wait() 总是阻止调用者。
        • Signal() 要么释放阻塞线程(如果有的话),要么信号丢失,就好像它从未发生过一样。
        • 如果Signal() 释放阻塞的线程,调用者将产生监视器(Hoare 类型)或继续(Mesa 类型)。只有调用者或释放的线程之一可以继续,但不能同时继续。

        此信息来自:http://www.cs.mtu.edu/~shene/NSF-3/e-Book/MONITOR/sema-vs-monitor.html

        一些有用的资源:

        【讨论】:

        • 这通常很有趣,但并不直接适用于 Java,因此有点令人困惑。 Hoare 和 Mesa 监控什么?
        • 是的,问题是针对Java提出的,但答案与Java无关。
        猜你喜欢
        • 2013-05-30
        • 1970-01-01
        • 2017-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多