Java 何时以及如何使用wait()和notify()

Java 何时以及如何使用wait()和notify(),java,multithreading,wait,runnable,notify,Java,Multithreading,Wait,Runnable,Notify,我从某处找到了这个例子。现在,我试图理解wait()和notify()/notifyAll()的用法。在哪种情况下,我们为什么需要这个 class BlockingQueue<T> { private Queue<T> queue = new LinkedList<T>(); private int capacity; public BlockingQueue(int capacity) { this.capacit

我从某处找到了这个例子。现在,我试图理解
wait()
notify()/notifyAll()
的用法。在哪种情况下,我们为什么需要这个

class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while (queue.size() == capacity) {
             System.out.println("Waiting...");
            wait();

        }

        queue.add(element);
        notify(); // notifyAll() for multiple producer/consumer threads
    }

    public synchronized T take() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify(); // notifyAll() for multiple producer/consumer threads
        return item;
    }
}
然后,像下面这样运行线程

 @Override
    public void run() {
        // synchronized (this) {
        BlockingQueue<Integer> s = new BlockingQueue(10);
        for (int i = 0; i < 12; i++) {
            try {
                s.put(i);
                if (i > 9) {
                    System.out.println(Thread.currentThread().getName() + "  : " + s.take());
                }
                System.out.println(Thread.currentThread().getName() + " ExtendsThread : Counter : " + i);
            } //}
            //notify();
            catch (InterruptedException ex) {
                Logger.getLogger(ExtendsThread.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

    }
 ImplementsRunnable rc = new ImplementsRunnable();
        Thread t1 = new Thread(rc, "A");
        t1.start();

当我运行它时,它会在计数器:9之后卡住,并一直等待。有人告诉我这里出了什么问题吗?

你的概念有点缺陷。
BlockingQueue
可以作为生产者/消费者模式中的桥梁

也就是说,它允许一个线程向其写入内容,另一个线程从中读取内容,但它的执行方式非常遥远,如果:

  • 没有要提取的项目,它将等待新项目到达
  • 如果项目太多,它将等待删除项目

在这种情况下,
wait
notify
BlockingQueue
实例的内部消息传递

你可以看看

所以,不要只使用一个线程,你应该使用(至少)两个线程,一个产品和一个消费者

制作人 这将获取
BlockingQueue
的一个实例,并向其添加
int
值。每次在添加下一个前停止1秒

public class Producer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        for (int index = 0; index < 10; index++) {
            try {
                System.out.println("Put " + index);
                queue.put(index);
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
            }
        }
    }

}
你可以使用类似于

BlockingQueue bq = new BlockingQueue(10);
Thread p = new Thread(new Producer(bq));
Thread c = new Thread(new Consumer(bq));
c.setDaemon(true);
c.start();
p.start();
您应该注意,
put
消息之间有一点延迟,但是
take
消息之间几乎没有延迟。这是正在操作的队列。
消费者
正在阻止/等待队列中的内容

您可以与
生产者
消费者
一起玩,可能会改变他们的时间(例如,在拿某个项目之前,
消费者
的延迟时间较长),以了解这可能会如何产生不同的效果

当我运行它时,它被卡在计数器9后,并一直等待

这可能是因为您已经超过了队列的容量,并且它的
put
方法一直处于阻塞状态,直到您从队列中取出某些内容为止(实际上,您有一个死锁,队列等待您从队列中取出一些内容,但您不能,因为您被锁定在
put

要记住的事情:

  • 要使两个或多个线程使用监视器锁,它们必须共享监视器/对象锁的同一实例。在这种情况下,
    BlockingQueue的相同实例
  • notify
    将唤醒一个对象,该对象正在监视器锁的
    wait
    方法的同一实例上等待。没有办法知道是哪一个。例如,如果您有多个使用者,但不关心数据的处理顺序,那么这可能很有用
更新了其他示例

因此,这将
线程.sleep
生产者中取出(并允许生产者产生100个值),并向
消费者添加
线程.sleep

这样,
生产者
将在
消费者
耗尽之前达到其容量,迫使其等待,直到
消费者
可以从中获取值

public class Producer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        for (int index = 0; index < 100; index++) {
            try {
                System.out.println("Put " + index);
                queue.put(index);
            } catch (InterruptedException ex) {
            }
        }
    }

}

public class Consumer implements Runnable {

    private BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                Integer value = queue.take();
                System.out.println("Took " + value);
                Thread.sleep(1000);
            }
        } catch (InterruptedException ex) {
        }
    }

}
公共类生成器实现可运行{
私有阻塞队列;
公共生产者(封锁队列){
this.queue=队列;
}
@凌驾
公开募捐{
对于(int-index=0;index<100;index++){
试一试{
System.out.println(“Put”+索引);
队列放置(索引);
}捕获(中断异常例外){
}
}
}
}
公共类使用者实现可运行{
私有阻塞队列;
公共消费者(封锁队列){
this.queue=队列;
}
@凌驾
公开募捐{
试一试{
while(true){
整数值=queue.take();
System.out.println(“take”+值);
睡眠(1000);
}
}捕获(中断异常例外){
}
}
}
在此处添加println

public synchronized void put(T element) throws InterruptedException {
    while (queue.size() == capacity) {
        System.out.println("blocked");
        wait();
    }
    queue.add(element);
    notify(); // notifyAll() for multiple producer/consumer threads
    System.out.println("put "+ element);
}

public synchronized T take() throws InterruptedException {
    while (queue.isEmpty()) {
        wait();
    }
    T item = queue.remove();
    notify(); // notifyAll() for multiple producer/consumer threads
    System.out.println("removed " + item);
    return item;
}
然后运行这个测试

public static void main(String argv[]) throws Exception {
    final BlockingQueue q = new BlockingQueue(2);
    new Thread() {
        public void run() {
            try {
                Thread.sleep(5000);
                q.take();
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
    q.put(1);
    q.put(2);  // will block here until tread 2 takes an element and reduces the capacity
    q.put(3);   
}
它会打印出来

put 1
put 2
blocked
removed 1
put 3

我的理解wait()和notify()的实现有什么问题?您对这些方法的理解是什么?你认为他们会做什么?也许[这个SQ问题和公认的答案][1]会帮助你[1]:@SotiriosDelimanolis
wait()
会等待,
notify()
会通知等待的线程。我说的对吗?创建一个新的
Runnable
,将值放入
BlockingQueue
和一个
Runnable
,从
BlockingQueue
中获取值,传递两个
BlockQueue
的相同实例,启动它们并让它们运行…@MadProgrammer让我试试这个为什么它同时添加和删除?请纠正我的理解。我希望这段代码的工作方式是,它应该继续添加元素,直到达到极限,一旦达到极限,它应该等待,然后删除一个元素并添加一个元素。这就是我所期望的工作方式
wait
notify
wait
notify
通过不同的线程工作,如果您在同一个线程上尝试此方法,您最终会遇到问题,它会无限期阻塞。试试这个,在每次服用后,在消费者中放入一个
线程。sleep(5000)
,看看它到底起了什么作用。。这太完美了。让我了解代码流程。我会让你知道同样的:)谢谢。检查更新,它会将
线程移动到
消费者
,这允许
制作人
以尽可能快的速度运行。它还使用100个值(而不是10个值)填充
生产者,以确保达到容量限制;)为什么在
消费者
中使用
while(true)
?这不是意味着无限循环吗?你的代码工作正常,正是我所期望的。但是,我有一个疑问,
put 1
put 2
blocked
removed 1
put 3