Fear never builds the future,but hope does.
—— 24.5.25
多等待多唤醒问题
在多条线程同时消费同时等待时,会出现问题
BaoZiPu
package S77ThreadMoreWait; /* count和flag可以定义成包装类,但要记得给count和flag手动赋值 不然对于本案例来说,容易出现空指针异常 */ public class BaoZiPu { // 包子的数目count private int count; // 是否有包子flag private boolean flag; public BaoZiPu() { } public BaoZiPu(int count, boolean flag) { this.count = count; this.flag = flag; } // getCount改成消费包子,直接输出包子数量count public void getCount() { System.out.println("消费了第"+count+"个包子"); } // setCount改造成生产包子,count++ public void setCount() { count++; System.out.println("生产了第"+count+"个包子"); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
生产product线程
package S77ThreadMoreWait; // 实现Runnable接口 public class Product implements Runnable{ BaoZiPu baoZiPu = new BaoZiPu(); // 提供一个有参构造 public Product(BaoZiPu baoZiPu){ this.baoZiPu = baoZiPu; } @Override public void run() { // 定义一个死循环 while(true) { try { Thread.sleep(100L); }catch (InterruptedException e){ throw new RuntimeException(e); } // 同步代码块 synchronized (baoZiPu){ // 1.判断flag是否为true,如果是true,证明有包子,生产线程等待 if(baoZiPu.isFlag()==true){ try{ baoZiPu.wait(); }catch(InterruptedException e){ throw new RuntimeException(e); } } // 2.如果flag为false,证明没有包子,则要开始生产 baoZiPu.setCount(); // 3.改变flag为true baoZiPu.setFlag(true); // 4.唤醒所有等待线程 baoZiPu.notify(); } } } }
消费consumer线程
package S77ThreadMoreWait; public class Consumer implements Runnable{ BaoZiPu baoZiPu = new BaoZiPu(); // 提供一个有参构造 public Consumer(BaoZiPu baoZiPu){ this.baoZiPu = baoZiPu; } @Override public void run() { while(true) { try { Thread.sleep(100L); }catch (InterruptedException e){ throw new RuntimeException(e); } // 同步代码块 synchronized (baoZiPu){ // 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待 if(baoZiPu.isFlag()==false){ // 抛出异常 try{ baoZiPu.wait(); }catch(InterruptedException e){ throw new RuntimeException(e); } } // 2.如果flag为true,则要开始消费 baoZiPu.getCount(); // 3.改变flag为false,消费完了,没有包子了 baoZiPu.setFlag(false); // 4.唤醒所有等待线程 baoZiPu.notify(); } } } }
测试类
package S77ThreadMoreWait; public class Demo216Test { public static void main(String[] args) { // 变成同一个对象 BaoZiPu baoZiPu = new BaoZiPu(); // 把baozipu对象分别传给两个线程中 Product product = new Product(baoZiPu); Consumer consumer = new Consumer(baoZiPu); // 三条生产线程 new Thread(product).start(); new Thread(product).start(); new Thread(product).start(); // 三条消费线程 new Thread(consumer).start(); new Thread(consumer).start(); new Thread(consumer).start(); } }
运行结果:会出现连续生产和连续消费的行为
多条线程在同时等待时,上一条线程结束之后会随机唤醒一条线程,所以不能确定具体的顺序
解决尝试 — 唤醒全部线程 notifyAll
生产线程Product
package S77ThreadMoreWait; // 实现Runnable接口 public class Product implements Runnable{ BaoZiPu baoZiPu = new BaoZiPu(); // 提供一个有参构造 public Product(BaoZiPu baoZiPu){ this.baoZiPu = baoZiPu; } @Override public void run() { // 定义一个死循环 while(true) { try { Thread.sleep(100L); }catch (InterruptedException e){ throw new RuntimeException(e); } // 同步代码块 synchronized (baoZiPu){ // 1.判断flag是否为true,如果是true,证明有包子,生产线程等待 if(baoZiPu.isFlag()==true){ try{ baoZiPu.wait(); }catch(InterruptedException e){ throw new RuntimeException(e); } } // 2.如果flag为false,证明没有包子,则要开始生产 baoZiPu.setCount(); // 3.改变flag为true baoZiPu.setFlag(true); // 4.唤醒所有等待线程 baoZiPu.notifyAll(); } } } }
消费线程Consumer
package S77ThreadMoreWait; public class Consumer implements Runnable{ BaoZiPu baoZiPu = new BaoZiPu(); // 提供一个有参构造 public Consumer(BaoZiPu baoZiPu){ this.baoZiPu = baoZiPu; } @Override public void run() { while(true) { try { Thread.sleep(100L); }catch (InterruptedException e){ throw new RuntimeException(e); } // 同步代码块 synchronized (baoZiPu){ // 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待 if(baoZiPu.isFlag()==false){ // 抛出异常 try{ baoZiPu.wait(); }catch(InterruptedException e){ throw new RuntimeException(e); } } // 2.如果flag为true,则要开始消费 baoZiPu.getCount(); // 3.改变flag为false,消费完了,没有包子了 baoZiPu.setFlag(false); // 4.唤醒所有等待线程 baoZiPu.notifyAll(); } } } }
运行结果:会出现连续生产和连续消费的行为
解决尝试 — 把 if 改成 while
生产线程Product
package S77ThreadMoreWait; // 实现Runnable接口 public class Product implements Runnable{ BaoZiPu baoZiPu = new BaoZiPu(); // 提供一个有参构造 public Product(BaoZiPu baoZiPu){ this.baoZiPu = baoZiPu; } @Override public void run() { // 定义一个死循环 while(true) { try { Thread.sleep(100L); }catch (InterruptedException e){ throw new RuntimeException(e); } // 同步代码块 synchronized (baoZiPu){ // 1.判断flag是否为true,如果是true,证明有包子,生产线程等待 while (baoZiPu.isFlag()==true){ try{ baoZiPu.wait(); }catch(InterruptedException e){ throw new RuntimeException(e); } } // 2.如果flag为false,证明没有包子,则要开始生产 baoZiPu.setCount(); // 3.改变flag为true baoZiPu.setFlag(true); // 4.唤醒所有等待线程 baoZiPu.notifyAll(); } } } }
消费线程Consumer
package S77ThreadMoreWait; public class Consumer implements Runnable{ BaoZiPu baoZiPu = new BaoZiPu(); // 提供一个有参构造 public Consumer(BaoZiPu baoZiPu){ this.baoZiPu = baoZiPu; } @Override public void run() { while(true) { try { Thread.sleep(100L); }catch (InterruptedException e){ throw new RuntimeException(e); } // 同步代码块 synchronized (baoZiPu){ // 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待 while (baoZiPu.isFlag()==false){ // 抛出异常 try{ baoZiPu.wait(); }catch(InterruptedException e){ throw new RuntimeException(e); } } // 2.如果flag为true,则要开始消费 baoZiPu.getCount(); // 3.改变flag为false,消费完了,没有包子了 baoZiPu.setFlag(false); // 4.唤醒所有等待线程 baoZiPu.notifyAll(); } } } }
运行结果:不会出现连续生产和连续消费的行为
总结:notifyAll和while要一起执行,才可以保证我们的代码不会出现错误情况