关于使用if和while包裹wait方法

it2025-09-09  7

结论:在需要先判断条件,再使用wait方法的情况下,使用while。原因是:线程使用wait放弃锁后,线程会停在wait方法的位置,等到线程被notify唤醒,并重新获得锁,线程会继续执行wait方法后面的操作。如果使用if语句块,线程重新获取锁并执行完if块中的语句后,不会再判断一次if条件,而是直接执行if外的语句,从而引起错误的执行流程。 错误示例代码:

/** * Author: Aipande * Description:生产者消费者模型 * Date-Of-Create:2020/10/22-8:45 */ @SuppressWarnings("all") public class PCDemo { public static void main(String[] args) throws InterruptedException { //共享资源 Product product = new Product(0); //消费者 new Thread(()->{ synchronized (product){ while (true){ //判断 if (product.number<=0){ try { //期望唤醒生产者线程 product.notifyAll(); product.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //执行 product.sale(); } } },"B消费者").start(); //消费者 new Thread(()->{ synchronized (product){ while (true){ //判断 if (product.number<=0){ try { //期望唤醒生产者线程 product.notifyAll(); product.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //执行 product.sale(); } } },"C消费者").start(); TimeUnit.SECONDS.sleep(1); //生产者 new Thread(()->{ synchronized (product){ while (true){ //判断 if(product.number>0){ //等待 try { //唤醒消费者线程 product.notifyAll(); product.wait();//锁在此处释放以后,线程就停留在这里,等待获取锁后继续执行 } catch (InterruptedException e) { e.printStackTrace(); } } //执行 product.add(); } } },"A生产者").start(); } } class Product{ int number; public Product(int number) { this.number = number; } public void add(){ System.out.println(Thread.currentThread().getName()+"生产了一件商品,剩余"+(++number)); } public void sale(){ System.out.println(Thread.currentThread().getName()+"消费了一件商品,剩余"+(--number)); } }

如下结果:

B消费者消费了一件商品,剩余-1 C消费者消费了一件商品,剩余-2 B消费者消费了一件商品,剩余-3 C消费者消费了一件商品,剩余-4 B消费者消费了一件商品,剩余-5 C消费者消费了一件商品,剩余-6 B消费者消费了一件商品,剩余-7 C消费者消费了一件商品,剩余-8 ......

我们分析一下流程: 1.主线程休眠1秒,消费者B先获取锁,此时商品数量为0,B线程Notify没啥作用,B线程wait。 2.消费者C获取锁,此时商品数量为0,C线程notify唤醒B线程,然后wait。 3.消费者B线程被C线程唤醒,执行if块外的sale代码,此时商品数量为-1,然后又唤醒C线程,自己wait。 4.C线程被B线程唤醒,执行if块外sale代码,商品数量变为-2,然后再唤醒B线程,自己wait。 … 循环往复,就出现上述结果。 究其原因,还是线程被唤醒后,没有重新再校验if条件,所以使用while条件判断包裹wait更加合适。

最新回复(0)