关于多线程虚假唤醒的理解和解决办法

最近在学习多线程的时候遇到虚假唤醒的问题,网上虽然看到了很多的帖子,但是千篇一律,没法解决,最后查了很多资料终于找到解决办法!

这里引用一段狂神的代码

/**
* 线程之间的通信问题:生产者和消费者问题!	等待唤醒,通知唤醒
* 线程交替执行	A	B 操作同一个变量	num = 0
*A num+1
*B num-1
*/
public class A {
public static void main(String[] args) { 
	Data data = new Data();

	new Thread(()->{
		for (int i = 0; i < 10; i++) { 
		try {
		data.increment();
		} catch (InterruptedException e) { 
		e.printStackTrace();
		}
	}
},"A").start();

	new Thread(()->{
		for (int i = 0; i < 10; i++) { 
		try {
		data.decrement();
		} catch (InterruptedException e) { 
		e.printStackTrace();
		}
	}
},"B").start();
// 判断等待,业务,通知
class Data{ // 数字 资源类

private int number = 0;


//+1
	public synchronized void increment() throws InterruptedException { 
		if (number!=0){	//0
		// 等待
		this.wait();
}
	number++; 
	System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我+1完毕了
this.notifyAll();
}

//-1
	public synchronized void decrement() throws InterruptedException { 
	if (number==0){ // 1
	// 等待
	this.wait();
	}
	number--; 
	System.out.println(Thread.currentThread().getName()+"=>"+number);
	// 通知其他线程,我-1完毕了
	this.notifyAll();
	}
}

一般而言线程调用wait()方法后,需要其他线程调用notify,notifyAll方法后,线程才会从wait方法中返回, 而虚假唤醒(spurious wakeup)是指线程通过其他方式,从wait方法中返回。

举个例子,买票时,线程A买票,如果发现没有余票,则会调用wait方法,线程进入等待队列中,线程B进行退票操作,余票数量加一,然后调用notify 方法通知等待线程,此时线程A被唤醒执行购票操作。

从程序的顺序性来看 if (number==0)没有问题,但是为什么会出现虚假唤醒呢?

因为wait方法可以分为三个操作:

(1)释放锁并阻塞

(2)等待条件cond发生

(3)获取通知后,竞争获取锁

假设此时有线程A,C买票,线程A调用wait方法进入等待队列,线程C买票时发现线程B在退票,获取锁失败,线程C阻塞,进入阻塞队列,线程B退票时,余票数量+1(满足条件2 等待条件发生),线程B调用notify方法后,线程C马上竞争获取到锁,购票成功后余票为0,而线程A此时正处于wait方法醒来过程中的第三步(竞争获取锁获取锁),当线程C释放锁,线程A获取锁后,会执行购买的操作,而此时是没有余票的。

解决的办法是条件判断通过while(number==0)来解决,
以上的东西都好理解,问题就是这里,为什么使用一个while就可以解决呢??我查了资料之后才知道,原来while 循环当表达式为真,则执行下面的语句;语句执行完之后再判断表达式是否为真,如果为真,再次执行下面的语句;然后再判断表达式是否为真……就这样一直循环下去,直到表达式为假,跳出循环,所以当表达式为真的时候while还会判断一次,而if当表达式为真的时候直接向下走了,就不会再判断了,所以解决了虚拟唤醒的问题

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值