java - java中使用wait()和notify()的简单场景

标签 java wait notify

我能否获得一个完整的简单场景,即建议如何使用它的教程,特别是与队列一起使用?

最佳答案

wait()notify() 方法旨在提供一种机制,允许线程阻塞直到满足特定条件。为此,我假设您想要编写一个阻塞队列实现,其中您有一些固定大小的元素后备存储。

您要做的第一件事是确定您希望方法等待的条件。在这种情况下,您将希望 put() 方法阻塞,直到存储中有空闲空间,并且您希望 take() 方法阻塞,直到那里是一些要返回的元素。

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while(queue.size() == capacity) {
            wait();
        }

        queue.add(element);
        notify(); // notifyAll() for multiple producer/consumer threads
    }

    public synchronized T take() throws InterruptedException {
        while(queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify(); // notifyAll() for multiple producer/consumer threads
        return item;
    }
}

关于必须使用等待和通知机制的方式,有几点需要注意。

首先,您需要确保对 wait()notify() 的任何调用都在同步的代码区域内(使用 wait() notify() 调用在同一个对象上同步)。造成这种情况的原因(除了标准线程安全问题)是由于被称为丢失信号的东西。

这方面的一个例子是,一个线程可能会在队列恰好满时调用 put(),然后它会检查条件,发现队列已满,但是在它可以阻塞之前安排了另一个线程。然后第二个线程 take() 是队列中的一个元素,并通知等待线程队列不再满。但是,因为第一个线程已经检查了条件,所以它会在重新调度后简单地调用 wait(),即使它可以取得进展。

通过在共享对象上进行同步,您可以确保不会发生此问题,因为在第一个线程实际阻塞之前,第二个线程的 take() 调用将无法进行。

其次,由于被称为虚假唤醒的问题,您需要将要检查的条件放在 while 循环中,而不是 if 语句中。这就是有时可以重新激活等待线程而不调用 notify() 的地方。把这个检查放在一个while循环中会确保如果发生虚假唤醒,会重新检查条件,线程会再次调用wait()


正如其他一些答案所提到的,Java 1.5 引入了一个新的并发库(在 java.util.concurrent 包中),旨在提供对等待/通知的更高级别的抽象机制。使用这些新功能,您可以像这样重写原始示例:

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(T element) throws InterruptedException {
        lock.lock();
        try {
            while(queue.size() == capacity) {
                notFull.await();
            }

            queue.add(element);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while(queue.isEmpty()) {
                notEmpty.await();
            }

            T item = queue.remove();
            notFull.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

当然,如果你真的需要一个阻塞队列,那么你应该使用 BlockingQueue 的实现。界面。

另外,对于这样的东西,我强烈推荐 Java Concurrency in Practice ,因为它涵盖了您可能想了解的有关并发相关问题和解决方案的所有信息。

关于java - java中使用wait()和notify()的简单场景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2536692/

相关文章:

java - 带等待/通知和不带它们的同步块(synchronized block)之间的区别?

Java 线程等待通知

java - 如果其他线程正在等待,线程是否会隐式调用 notifyAll() ?

JavaEE 8、Tomcat 8.5、错误状态 404

java - 为什么在 finally block 中更改返回的变量不会更改返回值?

java - google.android.gms.checkin.CheckinService 已泄漏 ServiceConnection 错误

使用 wait-notify 更新 Java 子类

Java终于回归了,奇怪的字节码

c - Waitpid 永远阻塞

java - 调用notifyAll有什么好的解决方案吗?