问题描述
我可以获得一个完整的简单场景,即教程,建议如何使用它,特别是使用队列吗?
wait()
和 notify()
方法旨在提供一种机制,允许线程阻塞直到特定条件得到满足。为此,我假设你想要编写一个阻塞队列实现,你有一些固定大小的元素后备存储。
你要做的第一件事就是确定您希望方法等待的条件。在这种情况下,您需要阻止 put()
方法,直到商店中有可用空间,并且您将需要 take()
阻止直到有一些要返回的元素的方法。
public class BlockingQueue< T> {
private Queue< T> queue = new LinkedList< T>();
私人产能;
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()
}
公共同步T take()抛出InterruptedException {
while(queue.isEmpty()){
等待();
}
T item = queue.remove();
notify(); //用于多个生产者/消费者线程的notifyAll()
返回项目;
}
}
有几点需要注意的方法您必须使用等待和通知机制。
首先,您需要确保对的任何调用wait()
或通知( )
在同步的代码区域内(使用 wait()
和 notify()
调用在同一对象上同步)。其原因(除了标准的线程安全问题)是由于被称为遗漏信号的原因。
这样做的一个例子是,当队列恰好满时,线程可以调用 put()
,然后检查条件,看到队列已满,但是在它可以阻止另一个线程被调度之前。第二个线程然后 take()
是队列中的一个元素,并通知等待的线程队列不再满。因为第一个线程已经检查了条件,所以在重新安排之后它将简单地调用 wait()
,即使它可以取得进展。
通过同步共享对象,可以确保不会发生此问题,因为第二个线程的 take()
调用将不会能够在第一个线程实际被阻止之前取得进展。
其次,你需要把你正在检查的条件放在while循环中,而不是if语句,到期一个被称为虚假叫醒的问题。这是在没有调用 notify()
的情况下有时可以重新激活等待线程的地方。将此检查放入while循环将确保如果发生虚假唤醒,将重新检查该条件,并且线程将再次调用 wait()
。 / p>
正如其他一些答案所提到的,Java 1.5引入了一个新的并发库(在中) java.util.concurrent
package),旨在提供对等待/通知机制的更高级别的抽象。使用这些新功能,您可以像这样重写原始示例:
public class BlockingQueue< T> {
private Queue< T> queue = new LinkedList< T>();
私人产能;
private Lock lock = new ReentrantLock();
private条件notFull = lock.newCondition();
private条件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()抛出InterruptedException {
lock.lock();
try {
while(queue.isEmpty()){
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
退货;
} finally {
lock.unlock();
}
}
}
当然如果您确实需要阻塞队列,那么您应该使用界面。
另外,对于这样的东西,我强烈推荐,因为它涵盖了您可能想知道的一切关于并发相关的问题和解决方案。
Can I get a complete simple scenario i.e. tutorial that suggest how this should be used, specifically with a Queue?
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.
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.
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.
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.
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.
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.
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();
}
}
}
Of course if you actually need a blocking queue, then you should use an implementation of the BlockingQueue interface.
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()的简单方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!