Jar*_*ell 267
的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;
}
}
Run Code Online (Sandbox Code Playgroud)
关于必须使用等待和通知机制的方式,有几点需要注意.
首先,需要确保任何呼叫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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果您确实需要阻塞队列,那么您应该使用BlockingQueue接口的实现 .
此外,对于这样的东西,我强烈推荐实践中的Java Concurrency,因为它涵盖了您可能想要了解的有关并发相关问题和解决方案的所有内容.
Enn*_*oji 147
不是队列的例子,但非常简单:)
class MyHouse {
private boolean pizzaArrived = false;
public void eatPizza(){
synchronized(this){
while(!pizzaArrived){
wait();
}
}
System.out.println("yumyum..");
}
public void pizzaGuy(){
synchronized(this){
this.pizzaArrived = true;
notifyAll();
}
}
}
Run Code Online (Sandbox Code Playgroud)
一些要点:
1)永远不要做
if(!pizzaArrived){
wait();
}
Run Code Online (Sandbox Code Playgroud)
总是使用while(条件),因为
while(!pizzaExists){ wait(); }.2)您必须在调用wait/nofity之前保持锁定(同步).线程还必须在唤醒之前获取锁定.
3)尽量避免在同步块中获取任何锁定,并努力不调用外来方法(您不知道他们正在做什么的方法).如果必须,请务必采取措施避免死锁.
4)注意notify().坚持使用notifyAll()直到你知道自己在做什么.
5)最后,但并非最不重要的,阅读Java Concurrency in Practice!
pol*_*nts 35
即使你要求wait()和notify()具体而言,我觉得这句话就够仍然很重要:
Josh Bloch,Effective Java第2版,第69项:首选并发实用程序wait和notify(强调他的):
鉴于使用
wait和notify正确使用的难度,您应该使用更高级别的并发实用程序来代替使用,wait并且notify直接就像"并发汇编语言"中的编程一样,与提供的更高级语言相比java.util.concurrent.很少,如果有的话,在新代码中使用wait和使用notify.
| 归档时间: |
|
| 查看次数: |
212861 次 |
| 最近记录: |