Java多线程问题:虚假唤醒

以一个普通的生产者消费者问题作为例子:

	//代码中的number 即为要消费和生产的产品
	//生产者
	public synchronized void increment() throws InterruptedException {
       	if(number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"生产了"+number+"产品");
        //生产产品后及时通知消费者来消费,否则死锁
        notifyAll();
    }
	//消费者
	public synchronized void decrement() throws InterruptedException {
        if(number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"消费了"+number+"产品");
        //消费产品后及时通知生产者开始生产,否则死锁
        notifyAll();
    }

上面代码,number作为产品,increment作为生产者,decrement作为消费者。

if(number==0)判断是否需要生产或消费,number只能为1或者0,如果只有一个生产者,一个消费者,是没有问题的。

如果现在有A,B两个生产者,A在进行了一次if判断后,进入了wait(),之后B进行了一次if判断后,也进入了wait()

然后消费者C调用了notifyAll(),唤醒了A,B两个生产者,假设生产者A拿到锁,进行生产后number=1并释放锁,此时由于多线程调度问题,消费者C没有来消费,但是生产者B却被调度到了,问题就发生在这里。

生产者B在执行wait()之前已经进行了if()判断,因此,生产者B会直接开始生产number=2

问题就这么发生了,生产者B是不应该被唤醒的,或者说,因为生产者A已经生产了,即使生产者B被唤醒,也不应该直接去生产,而是应该观察一下自己需要真的需要去生产。

如何解决?

只需要将if()换做while()即可,这样一来,生产者B即使被唤醒了,本身还处于while()循环之中,如果不需要生产,那么会继续wait()下去

仅为个人理解,如有不到之处恳请指正。

引用jdk1.8文档的一句话(java.lang.Object下的wait方法):

As in the one argument version, interrupts and spurious wakeups are
possible, and this method should always be used in a loop.
线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒 。 虽然这在实践中很少会发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。 换句话说,等待应该总是出现在循环中。

猜你喜欢

转载自blog.csdn.net/weixin_43967331/article/details/107992911
今日推荐