等待-唤醒模型中的wait、await应使用while判断

等待-唤醒模型中,之前一直使用if进行条件判断是否达到阀值来进行阻塞,比如下面的例子

 1 public class CoTest01 {
 2     public static void main(String[] args) {
 3         SynContainer container = new SynContainer();
 4         new Productor(container).start();
 5         new Consumer(container).start();
 6 //        new Productor(container).start();
 7 //        new Consumer(container).start();
 8     }
 9 }
10 //生产者
11 class Productor extends Thread{
12     SynContainer container  ;    
13     public Productor(SynContainer container) {
14         this.container = container;
15     }
16 
17     public void run() {
18         //生产
19         for(int i=0;i<100;i++) {
20             container.push(new Steamedbun(i));
21         }
22     }
23 }
24 //消费者
25 class Consumer extends Thread{
26     SynContainer container  ;    
27     public Consumer(SynContainer container) {
28         this.container = container;
29     }
30     public void run() {
31         //消费
32         for(int i=0;i<100;i++) {
33             container.pop();
34         }
35     }
36 }
37 //缓冲区
38 class SynContainer{
39     List<Steamedbun> list=new ArrayList<>(); //存储容器
40     //存储 生产
41     public synchronized void push(Steamedbun bun) {
42         //何时能生产  容器存在空间
43         //不能生产 只有等待
44         if(list.size() == 10) {
45             try {
46                 this.wait(); //线程阻塞  消费者通知生产解除
47             } catch (InterruptedException e) {
48                 System.out.println("push 异常");
49             }
50         }
51         //存在空间 可以生产
52         list.add(bun);
53         //存在数据了,可以通知消费了
54         this.notifyAll();
55         System.out.println("生产第-->"+list.size()+"个馒头");
56     }
57     //获取 消费
58     public synchronized void pop() {
59         //何时消费 容器中是否存在数据
60         //没有数据 只有等待
61         if(list.size() == 0) {
62             try {
63                 this.wait(); //线程阻塞  生产者通知消费解除
64             } catch (InterruptedException e) {
65             }
66         }
67         //存在数据可以消费
68         list.remove(list.size()-1);
69         this.notifyAll(); //存在空间了,可以唤醒对方生产了
70         System.out.println("消费第" + list.size() + "个馒头");
71     }
72 }
73 //馒头
74 class Steamedbun{
75     int id;
76     public Steamedbun(int id) {
77         this.id = id;
78     }
79     
80 }

这个例子是多个线程进行生产和消费馒头,馒头的个数不能小于0,大于10。

在44和61行使用的是if判断,此时main方法里创建的是两个线程,启动后发现并不会有异常。

 

 但是在改成四个线程后就发生异常了

 

 根据java8 api文档可知,可能会发生虚假唤醒,所以该方法应该始终在循环中使用。

Object 的 wait 方法

Condition 的 await 方法

 在换成while后,一切正常。

posted on 2020-11-10 20:32  萌新J  阅读(582)  评论(0编辑  收藏  举报