线程通信 wait()、notify(),if 和 while 条件,四线程同时等待导致死锁(notifyAll)

题外话:死锁的概念

一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁.
 

 

为什么要实现线程通信

 

当我们实现存钱取钱案例的时候,发现会产生很多的余额不足,是因为取钱的操作,因为不知道账户的余额,或者说不知道有存钱的操作,导致取钱的操作在不停的执行。如果说我们能让两个线程之间,能互相通信,知道对方的操作,就好了

等待:

必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线程会释放其所拥有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁,进入等待队列。

 

通知:唤醒线程,准确讲是随机唤醒一个处于等待队列中的线程

使用final 修饰的类:表示该类不能继承,修饰变量,表示值不能修改,修饰方法,表示方法不能重写

public final void notify

 

线程通知的案例

故事背景:对银行卡有这么两个操作,一个是存钱,一个是取钱。我们之前写实现的代码,创建了存钱和取钱的线程,但是在真正运行的时候,发现输出较多的没钱,请存钱,原因是两个线程之间没有特点的执行顺序,我们希望实现这样的一种顺序:当卡里有钱的时候,咱们就取钱,没钱了就存钱。我们对卡的操作者只有一个,另外一个放到等待队列中去。

这个代码很具有代表性,我们使用一个布尔值的flag,使用true表示有钱,可以进行取钱的操作。false:没钱,表示可以进行存钱的操作

我们使用的是同步方法,在方法前面加synchronized,所以我们就应该知道:锁的对象是this,当前对象。如果是static修饰方法,那么此时的对象就应该是类.class。关于这里不了解的可以看看这里,点击

我们先判断此时的flag是true或者false。存钱的话,里面有钱(true)我们就不存钱,所以让此线程处于等待队列:代码锁.wait()

如果flag为false,我们就执行下面的代码,就是存钱操作,记得存完钱之后还要将flag置为true

然后我们就可以使用 锁.notify()去唤醒取钱线程。

同理取钱的操作,就不介绍了

 

 

 

运行结果:

 

注意 当问题稍微变复杂一点,代码就会出现bug

比如,此时我们再创建两个线程对银行卡进行存取操作。此时有四个线程,两个存,两个取

运行代码:发现出现负数。为什么呢?

 

原因如下:这里冰冰和丽丽都是取钱,在某一时刻可能同时在等待队列中,notify只是随机唤醒一个线程,等到丽丽唤醒之后,取了1000,现在余额为0,然后就会去唤醒冰冰,然后冰冰也会去取钱,所以此时余额-1000.问题的根源就在于if判断

这里在判断为if成立之后,就会执行下面的语句,可是,如果就在判断完条件,就进入等待队列,重新唤醒之后,就不会再判断if 条件,就是说:唤醒之后的可能不满足条件都往下面执行了。所以修改代码

把 if 改为 while 

正常运行

 

但是这么修改之后,多次执行,又会出现新的问题,死锁

 

四线程都在等待

再来说一下现在的背景:

之前我们使用if判断,我们创建了两个线程,我们可以正常执行。当我们多创建了两个线程,出现了有负数余额的情况,我们就发现if判断有问题,会导致重新唤醒的线程不用判断if条件就执行下面的逻辑代码取钱的操作,导致余额为负数,所以我们改if 为while,就可以保证当线程重新唤醒之后,还在继续判断是否满足条件。这样就解决了余额为负数的问题。

但是我们多次运行程序之后,我们发现了程序陷入了死锁状态,四个线程都处于等待的状态

这种情况是怎么出现的呢??

存钱 A 和 B

取钱 C 和 D

此时C先判断条件,不符合,没钱取,进入等待队列 

然后D也来判断条件,不符合,没钱取,也进入等待队列。此时两个取钱的线程都在等待队列中

这时A来存钱了,满足条件,存钱,并且修改标记true,唤醒线程,比如唤醒了C,唤醒C不一定下一步C就执行

这时B也来存钱,发现里面有钱,就进入了等待队列

然后A又来存钱,发现里面也有钱,又进入了等待队列

C此时得到CPU和锁,进行取钱,取到了钱,修改标记为false,唤醒线程,问题就出在这里,比如唤醒了D,也是取钱操作。这里如果唤醒存钱操作,就不会出现死锁

D得到Cpu和锁,进行取钱,不能取,进入等待队列

C得到CPU,判断之后再次进入等待队列

所以 等待区就存在了 四个线程,陷入了死锁 

怎么办呢??

我们就把随机唤醒 修改为全部唤醒notifyAll()

程序总体效率变低了 

 

其实 这个存钱 与 取钱的问题,就是 和 生产 消费 问题 一样的

生产 和 消费者的问题 请看下一篇文章

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你在狗叫什么、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值