”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 为什么一个线程在notify()或notifyAll()之后总是重新获取对象锁?

为什么一个线程在notify()或notifyAll()之后总是重新获取对象锁?

发布于2024-11-16
浏览:312

Why Does One Thread Always Reacquire the Object Lock After notify() or notifyAll()?

notify()和notifyAll()之间的微妙区别

notify()和notifyAll()之间的主要区别在于它们唤醒的等待线程数量(一个与所有),这提出了另一个问题:

为什么一个线程总是重新获取对象锁?

一般来说这种情况下,notify()和notifyAll()都没有指定选择哪个等待线程重新获取锁。 JVM 或系统线程调度程序进行此选择,该选择可能是不确定的。

notifyAll() 的必要性

但是,在某些场景下使用 notify()可能会导致死锁,如下例所示:

使用notify()的生产者/消费者类

public class ProducerConsumer {

    private final int MAX_SIZE = 1;  // Buffer size

    private List buf = new ArrayList();

    public synchronized void put(Object o) {
        while (buf.size() == MAX_SIZE) {
            wait();
        }
        buf.add(o);
        notify();
    }

    public synchronized Object get() {
        while (buf.size() == 0) {
            wait();
        }
        Object o = buf.remove(0);
        notify();
        return o;
    }
}

死锁场景:

  1. 生产者 P1 将一个对象放入缓冲区。
  2. 生产者 P2 和 P3 尝试放入对象,但由于缓冲区已满而被阻止。
  3. 消费者 C1 尝试从缓冲区获取对象。
  4. C1 正在执行并获取对象,然后通知等待线程。
  5. P2或C2可以被通知唤醒,但它们在尝试重新获取锁时都被阻塞。
  6. C3在尝试获取锁时也被阻塞。

作为结果,所有三个线程都无限期地等待,导致死锁。

解决方案:notifyAll()

要解决这个死锁,必须使用notifyAll()而不是生产者/消费者代码中的notify()。这样可以确保所有等待线程都被唤醒,从而防止死锁。

推荐:

对于大多数场景,notifyAll() 是首选方法,因为它可以避免潜在的死锁。如果特定场景只需要唤醒一个特定的等待线程,那么notify()可谨慎使用。

最新教程 更多>

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3