在下面的代码中,我有一个关于调用wait()后会发生什么的问题。在我的代码中,我在调用wait()
后返回一个值,这实际上是做什么的?我认为调用wait()
会挂起当前线程,但如果调用i
而不返回false,则传递给addWorkItem(Integer i)
的值wait()
会发生什么?您可以在生产者线程中看到它将i
添加到重试缓冲区,如果它无法添加到双端队列中。如果等待后我没有返回false,那么值i
是否会丢失,或者一旦线程被唤醒它是否仍然存在?
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
public class ConsumerProducer2 {
private static int QUEUE_SIZE = 10;
private Deque<Integer> queue = new ArrayDeque<Integer>(QUEUE_SIZE);
public synchronized boolean addWorkItem(Integer i) {
while (queue.size() >= QUEUE_SIZE) {
try {
wait();
return false; // WHAT HAPPENS HERE?
} catch (InterruptedException ex) {}
}
queue.addLast(i);
notify();
return true;
}
public synchronized Integer getWork() {
while (queue.size() == 0) {
try {
wait();
return null; // WHAT HAPPENS HERE?
} catch (InterruptedException ex) {
}
}
Integer i = queue.removeFirst();
notify();
return i;
}
public static void main(String[] args) {
new ConsumerProducer2().go();
}
public void go() {
ConsumerThread ct = new ConsumerThread();
ct.start();
ConsumerThread ct2 = new ConsumerThread();
ct2.start();
ProducerThread pt = new ProducerThread();
pt.start();
}
class ConsumerThread extends Thread {
public void run() {
while(true) {
Integer work = getWork();
if (work == null) {
} else {
System.out.println("Thread: " + this.getId() + " received work: " + work);
}
}
}
}
class ProducerThread extends Thread {
private List<Integer> retryList = new ArrayList<Integer>();
public void run() {
while(true) {
Integer currWork;
if (retryList.size() == 0) {
currWork = (int) (Math.random() * 100);
} else {
currWork = retryList.remove(0);
System.out.println("Thread: " + this.getId() + " retrying old work: " + currWork);
}
if (!addWorkItem(currWork)) {
System.out.println("Thread: " + this.getId() + " could not add work (because buffer is probably full): " + currWork);
retryList.add(currWork);
} else {
System.out.println("Thread: " + this.getId() + " added work to queue: " + currWork);
}
}
}
}
}
答案 0 :(得分:2)
让生产者维护一个重试缓冲区确保i值不会丢失,但这仍然不是编写该方法的好方法。
从while循环内部返回没有意义。你检查队列的大小,如果它已经达到最大值,你就等到你得到队列大小改变的通知,然后莫名其妙地返回false(??)。等待并没有真正完成任何事情。
addWorkItem中的等待点是延迟线程,直到队列有新值的空间。你应该在循环中等待,当你退出等待时,你的线程重新获取锁并重新检查条件(队列大小&gt; max)以查看它是否还可以添加项目。
一旦线程从while循环退出,它就会持有锁,它确定新项目的队列中有足够的空间(因为没有其他线程可以做任何事情来改变队列的大小,而这个线程有锁定),它可以继续并将值添加到队列中。
你正以非生产性的方式捕获InterruptedException,因为你抓住了它,不用费心去恢复中断标志,然后回到while循环的顶部。你应该使用中断来退出等待并退出方法。让InterruptedException抛出这里会更有意义;运行该方法的线程应该比该对象更好地了解如何处理中断。
您不应该假设等待仅在通知线程时返回,它可以在没有通知的情况下返回。这是在循环中调用wait的原因之一。
返工版本:
public synchronized boolean addWorkItem(Integer i) throws InterruptedException {
while (queue.size() >= QUEUE_SIZE) {
wait();
}
queue.addLast(i);
notify();
return true;
}
如果你想借口从这里返回false,你可以让方法返回false如果队列在一段时间内没有为新条目腾出空间(超时可能是很多真正的好事) - 生活情况):
public synchronized boolean addWorkItem(Integer i) throws InterruptedException {
final long maxWaitTime = 60L * 1000;
long totalWaitTime = 0;
while (queue.size() >= QUEUE_SIZE && totalWaitTime <= maxWaitTime) {
long waitStartTime = System.currentTimeMillis();
wait(maxWaitTime);
totalWaitTime += (System.currentTimeMillis() - waitStartTime);
}
if (queue.size() >= QUEUE_SIZE) {
return false;
}
queue.addLast(i);
notify();
return true;
}
这仍然会使用重试缓冲区(它上面的第一个版本根本不会这样做),但可能没有你现在那么多。
另一件事:你有生产者和消费者线程同时访问它,并且两种情况都会调用notify。由于通知仅唤醒一个线程,因此线程可能会获得与其无关的通知(因此通知的线程会唤醒,检查其状况并发现它仍然是假的,然后等待更多,而另一个线程,通知实际上很重要,从来没有发现它)。有不同的方法来解决问题,你可以
分配单独的锁,一个用于生产者,一个用于消费者,
减少传递给wait方法的超时时间,以减少对获得通知的依赖,或者
您可以使用notifyAll(性能较差但快速修复)。
答案 1 :(得分:1)
查看this。
短篇小说:等待线程可以被另一个调用通知唤醒。因此,在您的情况下,addWorkItem将在另一个线程调用wait()
后调用notify()
的线程中返回false。
另外看看你的逻辑我觉得你在队列为空时试图阻止消费者,并在有工作要做的时候唤醒它。 并且您希望生产者在队列为空之前不要提供新的工作。 如果是这种情况,那么在等待之后调用return将关闭你的消费者/生产者,而不是让他们尽可能地完成他们的工作。