等待Java的条件

时间:2010-02-28 10:19:57

标签: java multithreading synchronization

我想制作一个线程,当它变为空时将值放入队列并等待这个条件,而不是。这是我尝试使用的代码,但它打印

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

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

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();
    }
}

4 个答案:

答案 0 :(得分:3)

wait()永远不会收到通知。

答案 1 :(得分:1)

wait()将永远继续,因为你从不调用notify()。

您可以等待队列并在您希望等待线程唤醒时调用notify。要执行此操作,您可以将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");
    }

将消费者改为:

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

正如史蒂夫在他的回答中所说,你也可以在消费者线程中使用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();
    }
}

答案 2 :(得分:0)

您使用BlockingQueue的Sice,您不必使用synchronized,因为默认情况下BlockingQueue是同步的。如果要使用同步,则应该通过同一对象进行同步:

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

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

        theSameObjectInstance.notifyAll();
    }
 }

并且消费者的方法应该包含在synchronized(theSameObjectInstance)中以便接收通知,消费者也应该“等待”某个地方,例如当qq为空时。

答案 3 :(得分:0)

您的要求声明您打算在空的时候将放入队列 - 我接受的是几个值,而不仅仅是一个。如果您的要求略有变化,表示您将一个项目放入队列并等到消耗之后再放入另一个项目,那么您就可以使用java.util.concurrent.Exchanger了。

它的行为类似于深度为BlockingQueue的行为,但它更多一点:它以两种方式传递一个对象,每个参与者都是“生产者”和“消费者”。因此,Exchanger将不接受“生产者”提供的物品,直到“消费者”也准备好提供物品。对于“制片人”来说,这不是“火与忘记”;生产和消费时间是互锁的。这可以防止实际的生产者淹没消费者的工作队列 - 再次,如BlockingQueue - 但它也会阻止生产者,直到消费者完成最后一轮工作。

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