假设有一个共享资源库 ResourcePool,它内部维护了两类资源:ResourceTypeA 和 ResourceTypeB。现在有两个线程 Thread1 和 Thread2,它们都需要从资源库中分别获取一种资源才能继续执行。Thread1 需要 ResourceTypeA 而 Thread2 需要 ResourceTypeB。资源库提供了等待和通知机制,允许线程在资源不可用时等待,并在资源变为可用时得到通知。
初始条件
ResourcePool中ResourceTypeA和ResourceTypeB都不可用。Thread1首先到达,开始等待ResourceTypeA。Thread2随后到达,开始等待ResourceTypeB。
死锁发生过程
-
资源释放与等待
ResourceTypeA变为可用,此时ResourcePool应该通知等待的线程。ResourcePool使用notify()方法尝试唤醒一个等待的线程。
-
错误的线程被唤醒
- 假设
notify()方法随机唤醒了Thread2而不是Thread1。 Thread2尝试获取ResourceTypeB,但是它仍然不可用,因此Thread2无法继续执行,它再次进入等待状态。
- 假设
-
正确的线程未被唤醒
Thread1,它实际上可以使用现在可用的ResourceTypeA,却没有被唤醒,因此它继续等待。
-
没有更多的通知
- 由于
ResourcePool只调用了一次notify(),并且没有新的资源变为可用来触发额外的通知,Thread1将永远等待下去,即使它需要的资源已经可用。
- 由于
-
死锁发生
Thread1和Thread2都在等待,无法继续执行,也无法唤醒对方,系统进入死锁状态。
死锁的解决
- 使用
notifyAll()替代notify(),这样在ResourceTypeA变为可用时,所有等待的线程都会被唤醒。Thread1和Thread2都有机会检查它们等待的资源是否可用,并相应地继续执行或者继续等待。 - 设计资源获取逻辑时使用循环来检查条件,并在条件不满足时继续等待(超时等待)。这样即使被错误唤醒,线程也会再次检查资源是否满足其需求,并在不满足时重新等待,而不是假设资源已经可用并进入死锁状态。
通过这种方式,可以确保每个线程在其所需资源变为可用时能够被唤醒并继续执行,从而避免了死锁的发生。