调用wait()后返回值是什么意思?

时间:2016-01-06 21:41:24

标签: java multithreading synchronization wait

在下面的代码中,我有一个关于调用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);
                }
            }
        }
    }
}

2 个答案:

答案 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将关闭你的消费者/生产者,而不是让他们尽可能地完成他们的工作。