Fear never builds the future,but hope does.
—— 24.5.25
多等待多唤醒问题
在多条线程同时消费同时等待时,会出现问题
BaoZiPu
package S77ThreadMoreWait;/*count和flag可以定义成包装类,但要记得给count和flag手动赋值不然对于本案例来说,容易出现空指针异常*/ public class BaoZiPu {// 包子的数目countprivate int count;// 是否有包子flagprivate boolean flag;public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}// getCount改成消费包子,直接输出包子数量countpublic 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;}@Overridepublic 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为truebaoZiPu.setFlag(true);// 4.唤醒所有等待线程baoZiPu.notify();}}} }
消费consumer线程
package S77ThreadMoreWait;public class Consumer implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Consumer(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic 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;}@Overridepublic 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为truebaoZiPu.setFlag(true);// 4.唤醒所有等待线程baoZiPu.notifyAll();}}} }
消费线程Consumer
package S77ThreadMoreWait;public class Consumer implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Consumer(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic 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;}@Overridepublic 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为truebaoZiPu.setFlag(true);// 4.唤醒所有等待线程baoZiPu.notifyAll();}}} }
消费线程Consumer
package S77ThreadMoreWait;public class Consumer implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Consumer(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic 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要一起执行,才可以保证我们的代码不会出现错误情况