【问题标题】:wait() does not free all current locks, how it can be done?wait() 不会释放所有当前的锁,它是如何做到的?
【发布时间】:2019-04-15 21:49:09
【问题描述】:

根据Java doc,当obj1.wait() 发生时,“线程释放此监视器的所有权并等待直到另一个线程通知...”

因此,obj1 的锁被释放,而当前线程正在等待。但是所有其他锁呢?这段代码可能会锁定两个对象:

synchronized(obj2){
    f1();
    synchronized(obj1){
        f2();
        obj1.wait();
    }
}

Obj2 不会被释放,但线程没有运行,另一个线程将徒劳地等待释放 obj2……我不明白 obj2 继续锁定的原因。但是,好的,它就是这样。

但是我怎样才能更好地组织这个等待,我怎样才能在等待所有或至少几个当前锁的时候解锁呢?

【问题讨论】:

  • 我手边没有我的 JCIP 书,所以我不能说推荐的修复方法是什么,但我怀疑你必须手动退出所有同步块才能释放锁.
  • @markspace 根据docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait(),您正在为其调用wait() 的对象已被释放。无需离开。
  • 除非obj2 似乎不会,所以这就是为什么您需要离开以释放锁定。
  • 您可以使用LockCondition 接口的实现,而不是使用监视器锁。如果obj2Lock,您可以在等待obj1 之前释放它。
  • @markspace 是的,我对 obj2 感兴趣。那么,解决方案是不要在代码中使用 wait() ,由多个同步覆盖?没有 Locks 或 Futures 或其他帮助?也许使用 wait() 以外的其他方法会有所帮助?

标签: java multithreading


【解决方案1】:

您可以使用比sychronized 语句更灵活的Locks 和Conditions。

对于您的示例,您可以将 obj2 替换为 ReentrantLock

Lock lock2 = new ReentrantLock();

try {
    // Blocks until the lock is acquired, just like a `synchronized` statement
    lock2.lock();
    f1();

    synchronized (obj1) {
        f2();
        lock2.unlock();
        obj1.wait();
        lock2.lock();
    }
}
// Use `finally` to make sure the lock is always released, even if an exception is thrown
finally {
    // Exception might have been thrown before current thread could acquire lock again, cannot 
    // unlock then
    if (lock2.isHeldByCurrentThread()) {
        lock2.unlock();
    }
}

但是,这将允许另一个线程在当前线程开始等待obj1 之前获取lock2。如果不希望这样做,您可以将obj1 替换为Lock,然后等待obj2

【讨论】:

  • 谢谢,更简单了。但我认为源问题的出现是因为多线程使用了不同的层。等待/通知 - 同步 - 锁定/条件。恕我直言,纯锁定/条件解决方案会更好。
【解决方案2】:

但是所有其他锁呢?...我不明白 obj2 持续锁定的原因

Java 编译器和运行时库都不知道解锁obj2 是否安全。锁定对象的全部原因是为了防止其他线程看到处于不一致状态的共享数据。您写synchronized (obj2) {...} 的唯一原因是您的程序将在obj2 被锁定时执行某些操作,除了正在执行此操作的线程之外,其他所有线程都应该对其隐藏。

程序员有责任知道什么时候已经清理了烂摊子以及解锁锁的安全性。编译器无法为您做到这一点。


那么,您可能会问,为什么obj1.wait() 会解锁obj1?那是因为,如果您以预期使用的方式使用wait()notify(),则解锁必须是原子的等待。在第一个线程解锁obj1 和它进入等待状态的那一刻之间,其他线程不可能溜进来并传递obj1.notify()。如果这是可能的,那么通知可能会丢失,程序会挂起。

您可以通过阅读 Oracle 的“受保护块”教程了解所有关于 wait()notify() 的工作方式以及它们应该如何使用的信息。

https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

【讨论】:

  • 你认为这个线程,在等待obj1的过程中,可以改变什么吗?它什么都不做,有任何数据。它只是在等待通知或超时。
  • @Gangnus,不,我不这么认为。 obj1obj2 是不同的对象。 obj1.wait()应该解锁obj2没有充分的理由,而我上面试图说的是充分的理由应该不 解锁obj2。另外值得注意的是:Java 语言编译器和运行时库没有你那么聪明。他们无法像你所知道的那样了解你的代码是如何工作的——关于什么是安全的,什么是不安全的。他们在安全方面犯了错误,不解锁obj2比解锁更安全。
猜你喜欢
  • 2018-11-13
  • 1970-01-01
  • 2018-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-14
  • 1970-01-01
相关资源
最近更新 更多