在 Java 中等待条件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2350737/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 20:43:57  来源:igfitidea点击:

Waiting for condition in Java

javamultithreadingsynchronization

提问by Fluffy

I want to make one thread that puts values to a queue when it gets empty and wait for this condition while it is not. Here's the code I've tried to use, but it prints

我想创建一个线程,当它变空时将值放入队列,并在它不是时等待这种情况。这是我尝试使用的代码,但它会打印

Adding new
Taking Value 1
Taking Value 2
Taking Value 3
Taking Value 4

So it is working only one time. What is the problem?

所以它只工作一次。问题是什么?

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;


public class SO {
    public String test;
    public String[] list = new String[] {test};

    public static void main(String[] args) {
        new SO();
    }

    public SO() {
        go();
    }

    BlockingQueue<String> qq = new LinkedBlockingQueue<String>();

    class Producer implements Runnable {
        public void run() {
            try {
                while (true) {
                    synchronized (this) {
                        while (qq.size() > 0)
                            wait();

                        System.out.println("Adding new");
                        qq.put("Value 1");
                        qq.put("Value 2");
                        qq.put("Value 3");
                        qq.put("Value 4");
                    }
                }
            } catch (InterruptedException ex) {}
        }
    }

    class Consumer implements Runnable {
        public void run() {
            try {
                while(true) {
                    System.out.println("Taking " + qq.take()+". "+String.valueOf(qq.size())+" left");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException ex) {}
        }
    }

    public void go() {
        Producer p = new Producer();
        Consumer c = new Consumer();

        new Thread(p).start();
        new Thread(c).start();
    }
}

回答by Stephen Denne

wait()is never notified.

wait()从不通知。

回答by Richard Miskin

The wait() will continue forever because you never call notify().

wait() 将永远持续下去,因为您从不调用 notify()。

You could wait on the queue and call notify on that when you want the waiting thread to wakeup. To do this you would change Producer to read:

您可以在队列中等待并在您希望等待线程唤醒时调用通知。为此,您需要将 Producer 更改为:

    synchronized (qq) {
        while (qq.size() > 0)
            qq.wait();

            System.out.println("Adding new");
            qq.put("Value 1");
            qq.put("Value 2");
            qq.put("Value 3");
            qq.put("Value 4");
    }

And change Consumer to read:

并将消费者更改为:

    while(true) {
        synchronized (qq) {
            System.out.println("Taking " + qq.take() + ". " + String.valueOf(qq.size()) + " left");
            qq.notify();
        }
        Thread.sleep(1000);
    }

As Steve says in his answer you could also use wait() in the consumer thread so it can wait until there is something in the list rather than sleeping. So your code would become:

正如史蒂夫在他的回答中所说,你也可以在消费者线程中使用 wait() ,这样它就可以等到列表中有东西而不是睡觉。所以你的代码会变成:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class SO {
    public String test;
    public String[] list = new String[] { test };

    public static void main(String[] args) {
        new SO();
    }

    public SO() {
        go();
    }

    BlockingQueue qq = new LinkedBlockingQueue();

    class Producer implements Runnable {
        public void run() {
            try {
                while (true) {
                    synchronized (qq) {
                        if (!qq.isEmpty()) {
                            qq.wait();
                        }

                        System.out.println("Adding new");
                        qq.put("Value 1");
                        qq.put("Value 2");
                        qq.put("Value 3");
                        qq.put("Value 4");
                        qq.notify();
                    }
                }
            } catch (InterruptedException ex) {
            }
        }
    }

    class Consumer implements Runnable {
        public void run() {
            try {
                while (true) {
                    synchronized (qq) {
                        System.out.println("Taking " + qq.take() + ". "
                                + String.valueOf(qq.size()) + " left");
                        if (qq.isEmpty()) {
                            qq.notify();
                            qq.wait();
                        }
                    }
                }
            } catch (InterruptedException ex) {
            }
        }
    }

    public void go() {
        Producer p = new Producer();
        Consumer c = new Consumer();

        new Thread(p).start();
        new Thread(c).start();
    }
}

回答by Steve

Sice you are using BlockingQueue, you don't have to use synchronized, because BlockingQueueis sychronized by default. If you want to use synchronization, than you should synchronize trough the same object:

您正在使用BlockingQueue,您不必使用synchronized,因为BlockingQueue默认情况下是同步的。如果你想使用同步,那么你应该通过同一个对象同步:

 synchronized(theSameObjectInstance) {
    while (true) {                        
        while (qq.size() > 0)
            theSameObjectInstance.wait();                            

        System.out.println("Adding new");
        qq.put("Value 1");
        ...

        theSameObjectInstance.notifyAll();
    }
 }

and consumer's method should be wrapped in synchronized(theSameObjectInstance)in order to recieve notification, consumer should also "wait" somewhere e.g. when qq is empty.

并且消费者的方法应该被包裹起来synchronized(theSameObjectInstance)以接收通知,消费者也应该在某处“等待”,例如当qq为空时。

回答by seh

Your requirements state that you intend to put valuesinto the queue when it's empty -- several values, I take it, rather than just one. If your requirements change slightly to say that you put oneitem in the queue and wait until it's consumed before putting another, then you'd be ripe for using java.util.concurrent.Exchanger.

您的要求表明您打算在队列为空时将放入队列中——我接受多个值,而不仅仅是一个。如果您的需求略有变化,即您将一项放入队列中并等到它被消耗后再放入另一项,那么您就可以使用java.util.concurrent.Exchanger.

Its behavior is similar to a BlockingQueueof depth one, but it does a little more: It passes an object both ways, where each participant is both a "producer" and a "consumer". Hence, an Exchangerwon't accept a "producer"'s offering of an item until the "consumer" is also ready to offer an item back. It's not "fire and forget" for the "producer"; the production and consumption timing is interlocked. This prevents an actual producer from flooding a consumer's work queue -- again, like BlockingQueue-- but it also stalls the producer until the consumer has completed the last round of work.

它的行为类似于BlockingQueue深度的行为,但它做得更多一点:它双向传递对象,其中每个参与者既是“生产者”又是“消费者”。因此,Exchanger在“消费者”也准备好回馈商品之前,不会接受“生产者”提供的商品。对于“制作人”来说,这不是“一劳永逸”;生产和消费时间是互锁的。这可以防止实际的生产者淹没消费者的工作队列——同样,就像BlockingQueue——但它也会阻止生产者,直到消费者完成最后一轮工作。

In your case, the consumer might not have anything useful to return to the producer. Regardless, you can form a protocol between the participants. When the producer wants the consumer thread to shut down, it can offer a null value. Once the consumer accepts the null value, the producer might do one more round of exchange to finalize the shutdown request and collect any final outcome from the consumer.

在您的情况下,消费者可能没有任何有用的东西可以返回给生产者。无论如何,您可以在参与者之间形成协议。当生产者希望消费者线程关闭时,它可以提供一个空值。一旦消费者接受空值,生产者可能会再进行一轮交换以完成关闭请求并从消费者那里收集任何最终结果。