java中使用wait()和notify()的简单场景 [英] A simple scenario using wait() and notify() in java

查看:62
本文介绍了java中使用wait()和notify()的简单场景的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Can I get a complete simple scenario i.e. tutorial that suggest how this should be used, specifically with a Queue?

推荐答案

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

The wait() and notify() methods are designed to provide a mechanism to allow a thread to block until a specific condition is met. For this I assume you're wanting to write a blocking queue implementation, where you have some fixed size backing-store of elements.

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

The first thing you have to do is to identify the conditions that you want the methods to wait for. In this case, you will want the put() method to block until there is free space in the store, and you will want the take() method to block until there is some element to return.

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;
    }
}

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

There are a few things to note about the way in which you must use the wait and notify mechanisms.

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

Firstly, you need to ensure that any calls to wait() or notify() are within a synchronized region of code (with the wait() and notify() calls being synchronized on the same object). The reason for this (other than the standard thread safety concerns) is due to something known as a missed signal.

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

An example of this, is that a thread may call put() when the queue happens to be full, it then checks the condition, sees that the queue is full, however before it can block another thread is scheduled. This second thread then take()'s an element from the queue, and notifies the waiting threads that the queue is no longer full. Because the first thread has already checked the condition however, it will simply call wait() after being re-scheduled, even though it could make progress.

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

By synchronizing on a shared object, you can ensure that this problem does not occur, as the second thread's take() call will not be able to make progress until the first thread has actually blocked.

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

Secondly, you need to put the condition you are checking in a while loop, rather than an if statement, due to a problem known as spurious wake-ups. This is where a waiting thread can sometimes be re-activated without notify() being called. Putting this check in a while loop will ensure that if a spurious wake-up occurs, the condition will be re-checked, and the thread will call wait() again.

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

As some of the other answers have mentioned, Java 1.5 introduced a new concurrency library (in the java.util.concurrent package) which was designed to provide a higher level abstraction over the wait/notify mechanism. Using these new features, you could rewrite the original example like so:

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 并发实践,因为它涵盖了您可能想了解的与并发相关的问题和解决方案的所有内容.

Also, for stuff like this I'd highly recommend Java Concurrency in Practice, as it covers everything you could want to know about concurrency related problems and solutions.

这篇关于java中使用wait()和notify()的简单场景的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆