【问题标题】:Why wait, notify and notifyAll are methods in Object class not like Cloneable为什么 wait、notify 和 notifyAll 是 Object 类中的方法,不像 Cloneable
【发布时间】:2017-08-21 16:44:58
【问题描述】:

最近在采访中被问到为什么使用waitnotifynotifyAll。我解释了他们。

之后他们要求我假设应用程序始终是单线程的。真的需要吗?我的回答是否定的。

然后,他们问为什么 waitnotifynotifyAll 这样的设计是 Object 类上的方法。为什么Java没有接口,而这些方法在该接口中,并且任何想要实现它的类都可以使用它。所以,我有点卡住了,无法思考这个设计。任何人都可以对此播种吗?

【问题讨论】:

标签: java multithreading oop jvm java-threads


【解决方案1】:

Java 程序是多线程的从一开始就是一个设计目标。请记住,Java 的计划是让嵌入式编程不那么令人生畏,整个服务器端 Web 应用程序(导致 Sun 的核心业务商品化)是个意外。

由于目标是创建能够与其他设备通信的嵌入式应用程序,因此它必须是多线程的,以便网络友好和事件驱动。但是编写高效的多线程服务器在 java 的列表中并不高。

Java 很长时间没有 ReentrantLock 或非阻塞 i/o。最初可用的主要数据结构是 Vector、Hashtable 和 StringBuffer(所有这些都已在所有公共方法上同步)。从那个选择看来,目标似乎足够好,而不是尽可能高效。后来很明显,Java 需要更高效地用于服务器应用程序的用例,并且 1.2 引入了不同步的 Vector 和 Hashtable 等价物。这似乎是事后才想到的,一旦发现 Java 有一个以前不是为它设计的新角色,就进行了课程调整。

如果 Java 一直停留在创建它的利基市场,那么内在锁可能就足够了。看起来最初的计划是只针对内部锁,所以锁也可以连接到对象中。

【讨论】:

    【解决方案2】:

    然后,他们问为什么 waitnotifynotifyAll 这样的设计是 Object 类上的方法。为什么Java没有接口,而这些方法在该接口中,并且任何想要实现它的类都可以使用它。

    所有这些方法都是在本机代码中实现的,它们与包装它们的synchronized 块紧密集成。它们是 Java 语言定义的一部分,具有程序员所依赖的特定行为。它们不适合作为任何对象都可以实现的接口方法。

    当一个对象在另一个对象上调用obj.wait();时,它不必担心wait的实现。它需要确保它对该对象具有互斥锁,以便可以对其或其他存储进行关键更新,并且如果 wait 方法是由对象本身实现的,那么该对象可能违反语言要求,并且,对于例如,允许多个线程同时进入受保护的块。线程可以synchronize 并在另一个对象上调用wait/notify/notifyAll,而不必担心该对象是否已适当地实现了这些方法。通过在Object 上设置final 方法,无论对象类型或本地实现如何,行为都将相同。

    另外,正如我所提到的,wait/notify/notifyAll 与周围的synchronized 块紧密集成。当一个线程在wait() 中被阻塞时,周围的synchronized 锁将被释放,以便其他线程可以访问受保护的块。如果wait() 只是一个没有其他奇怪语言特性的简单方法调用,这种协调是不可能的。

    这让我想起了我在这里的另一个答案:Concept behind putting wait(),notify() methods in Object class

    【讨论】:

    • 我会说共识——至少在编译器圈子里——是允许每个对象用于同步是一个代价高昂的错误,特别是考虑到同步的最佳实践(使用最终对象来确保锁不能泄露)。反对提供显式锁类(接口确实是一个奇怪的选择,因为只有有限的可扩展性才有意义)真正启用这些功能的好论据并不多。你不同意吗?
    • 对不起,我不同意。如果您不必使用它,它如何“昂贵”?当我希望 JVM 高效地支持它时,每个人都非常反对synchronized——也许使用人们想要手动完成的相同的Lock 对象。我想编写和维护更少的代码,并教我的初级程序员使用语言特性而不是直接使用 Lock 对象,除非我必须 @Voo。
    • 因为 JVM 必须支持每个对象的对象头中的锁,尽管只有 0.0..01% 的对象会真正使用它。现在,HotSpot 尤其竭尽全力将相关开销降至最低,但这种复杂性也有其自身的成本:那部分代码中存在许多错综复杂的错误。
    • 正如我所说,最好的做法是创建一个单独的对象,无论如何都要锁定。 private final Object lock = new Object()private final Lock lock = new Lock() 之间的区别充其量是最小的,可以清楚地分离职责,可以避免初学者的错误和糟糕设计的来源,并且可以进行大量自定义。
    • 仅仅因为 JRE/JVM 支持为每个对象创建锁的代码并不意味着对内存中的对象有影响@Voo。除非您在其上调用synchronized,否则您不会获得与该对象关联的监视器或锁。我可以通过多种方式看到它与new Lock()按需一样昂贵。
    【解决方案3】:

    Wait、notify 和 notifyAll 不仅仅是普通的方法或同步工具,它们是 Java 中两个线程之间的通信机制。如果此机制不能通过任何 java 关键字(如同步)使用,则 Object 类是使它们可用于每个对象的正确位置。记住同步和等待通知是两个不同的领域,不要混淆它们是相同的还是相关的。同步是为了提供互斥和保证Java类的线程安全,比如竞争条件,而等待和通知是两个线程之间的通信机制。

    【讨论】:

      【解决方案4】:

      JVM 使用操作系统级别的线程。这意味着每个具体操作系统的每个具体 JVM 处理线程的方式不同。并且这些方法不仅在Object类中实现,它们被标记为native,这意味着它们是在JVM的系统层实现的。

      如果这些方法在某个接口中,那意味着任何人都可以重新定义它们。

      【讨论】:

        猜你喜欢
        • 2010-12-18
        • 2019-02-19
        • 1970-01-01
        • 2013-02-23
        • 2013-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多