简体   繁体   English

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

[英]What does it mean to return a value after calling wait()?

In the code below I have a question regarding what happens after I call wait(). 在下面的代码中,我有一个关于调用wait()后会发生什么的问题。 In my code, I am returning a value after calling wait() , what does this actually do? 在我的代码中,我在调用wait()后返回一个值,这实际上是做什么的? I thought that calling wait() suspends the current thread, but what happens to the value i passed to addWorkItem(Integer i) if wait() is called without returning false? 我认为,调用wait()挂起当前线程,但会发生什么变化值i传递给addWorkItem(Integer i)如果wait()调用时没有返回false? You can see in the producer thread that it adds i to a retry buffer if it couldn't be added to the deque. 您可以在生产者线程中看到,如果无法将其添加到deque中,则会将i添加到重试缓冲区。 If I don't return false after wait, does the value i just get lost, or is it still there once the thread wakes up? 如果我不等待后返回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);
                }
            }
        }
    }
}

Having the producer maintain a retry buffer does keep the i value from getting lost, but this still isn't a good way to write the method. 让生产者维护重试缓冲区确实使i值不会丢失,但这仍然不是编写方法的好方法。

Returning from inside the while loop doesn't make sense. 从while循环内部返回没有意义。 You check the size of the queue, and if it's maxed out you wait around until you get a notification that the size of the queue changed, then inexplicably return false (??). 你检查队列的大小,如果它已经达到最大值,你就等到你得到队列大小改变的通知,然后莫名其妙地返回false(??)。 The waiting doesn't really accomplish anything. 等待并没有真正完成任何事情。

The point of waiting in addWorkItem is to delay your thread until the queue has room for the new value. addWorkItem中的等待点是延迟线程,直到队列有新值的空间。 You should wait inside a loop, where when you come out of the wait, your thread reacquires the lock and re-checks the condition (queue size > max) to see if it can add the item yet. 你应该在循环中等待,当你退出等待时,你的线程重新获取锁并重新检查条件(队列大小>最大),看它是否可以添加项目。

Once the thread has exited from the while loop it is holding the lock, it is sure there's enough room in the queue for the new item (because no other threads can do anything to change the size of the queue while this thread has the lock held), and it can go ahead and add the value to the queue. 一旦线程退出while循环,它就会持有锁,它确保新项目的队列中有足够的空间(因为没有其他线程可以做任何事情来改变队列的大小,而这个线程有锁定),它可以继续并将值添加到队列中。

You are catching the InterruptedException in an unproductive way, because you catch it, don't bother to restore the interrupt flag, and go back to the top of the while loop. 您正在以非生产性的方式捕获InterruptedException,因为您捕获它,不必费心恢复中断标志,并返回到while循环的顶部。 You should be using the interruption to quit waiting and get out of the method. 你应该使用中断来退出等待并退出方法。 Letting InterruptedException be thrown here would make more sense; 让InterruptedException抛出这里会更有意义; the thread running the method should know better how to handle the interruption than this object does. 运行该方法的线程应该比这个对象更好地了解如何处理中断。

You shouldn't assume wait returns only when the thread is notified, it can return without a notification. 您不应该假设等待仅在通知线程时返回,它可以在没有通知的情况下返回。 That's one of the reasons to call wait in a loop. 这是在循环中调用wait的原因之一。

Reworked version: 返工版本:

public synchronized boolean addWorkItem(Integer i) throws InterruptedException {
    while (queue.size() >= QUEUE_SIZE) {
        wait();
    }
    queue.addLast(i);
    notify();
    return true;
}

If you want an excuse to return false from this you could make the method return false if the queue doesn't make room for the new entry within some time frame (having a timeout can be a good thing in a lot of real-life situations): 如果你想借口从这里返回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;
}

This will still use the retry buffer (which the first version above it won't do at all), but probably not nearly as much as you are now. 这仍然会使用重试缓冲区(它上面的第一个版本根本不会执行),但可能不会像现在这样多。

Another thing: you have producer and consumer threads concurrently accessing this, and notify is called for both cases. 另一件事:你有生产者和消费者线程同时访问它,并且两种情况都会调用notify。 Since notify only wakes up one thread, it's possible for a thread to get a notification that isn't relevant for it (so the notified thread wakes up, checks its condition and finds it still false, then waits some more, while another thread that the notification actually matters to never finds out about it). 由于通知仅唤醒一个线程,因此线程可能会获得与其无关的通知(因此通知的线程会唤醒,检查其状况并发现它仍然是假的,然后等待更多,而另一个线程,通知实际上很重要,从来没有发现它)。 There are different ways to solve the problem, you can 你可以用不同的方法解决问题

  • assign separate locks, one for producers and one for consumers, 分配单独的锁,一个用于生产者,一个用于消费者,

  • reduce the timeout passed into the wait method so you're less dependent on getting notified, or 减少传递给wait方法的超时,这样你就不会依赖于获得通知,或者

  • you can use notifyAll (less performant but a quick fix). 你可以使用notifyAll(性能较差但快速修复)。

Have a look at this . 看看这个

Short story : A waiting thread can be woken up by another one calling notify. 简短的故事 :一个等待的线程可以被另一个呼叫通知唤醒。 So in your case addWorkItem will return false in a thread that called wait() just after another thread calls notify() . 所以在你的情况下,addWorkItem将在另一个线程调用notify()之后调用wait()的线程中返回false。

Also having a look at your logic I think you are trying to block the consumer when the queue is empty and awake it when there is job to be done. 另外看看你的逻辑我认为你在队列为空时试图阻止消费者,并在有工作要做的时候唤醒它。 And you want the producer not to deliver new jobs until the queue is empty. 并且您希望生产者在队列为空之前不要提供新的工作。 If this is the case, then calling return after waiting will just close your consumer/producer not letting them finish their jobs when they can. 如果是这种情况,那么在等待之后调用return将关闭你的消费者/生产者,而不是让他们尽可能地完成他们的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM