繁体   English   中英

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

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

在下面的代码中,我有一个关于调用wait()后会发生什么的问题。 在我的代码中,我在调用wait()后返回一个值,这实际上是做什么的? 我认为,调用wait()挂起当前线程,但会发生什么变化值i传递给addWorkItem(Integer i)如果wait()调用时没有返回false? 您可以在生产者线程中看到,如果无法将其添加到deque中,则会将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);
                }
            }
        }
    }
}

让生产者维护重试缓冲区确实使i值不会丢失,但这仍然不是编写方法的好方法。

从while循环内部返回没有意义。 你检查队列的大小,如果它已经达到最大值,你就等到你得到队列大小改变的通知,然后莫名其妙地返回false(??)。 等待并没有真正完成任何事情。

addWorkItem中的等待点是延迟线程,直到队列有新值的空间。 你应该在循环中等待,当你退出等待时,你的线程重新获取锁并重新检查条件(队列大小>最大),看它是否可以添加项目。

一旦线程退出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(性能较差但快速修复)。

看看这个

简短的故事 :一个等待的线程可以被另一个呼叫通知唤醒。 所以在你的情况下,addWorkItem将在另一个线程调用notify()之后调用wait()的线程中返回false。

另外看看你的逻辑我认为你在队列为空时试图阻止消费者,并在有工作要做的时候唤醒它。 并且您希望生产者在队列为空之前不要提供新的工作。 如果是这种情况,那么在等待之后调用return将关闭你的消费者/生产者,而不是让他们尽可能地完成他们的工作。

暂无
暂无

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

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