16.5.6 死锁
什么时候会发生死锁
当两个线程相互等待对方释放同步监视器时就会发生死锁,
发生死锁会怎样
Java
虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
程序示例
死锁是很容易发生的,尤其在系统中出现多个同步监视器的情况下,如下程序将会出现死锁。
1 | package thread; |
运行上面程序,将会看到如下效果,此时程序既无法向下执行,也不会抛出任何异常,就一直”僵持”着。
1 | 主线程 进入了A实例的firstA()方法 睡眠.zzz |
究其原因,是因为:上面程序中A对象和B对象的方法都是同步方法,也就是A对象和B对象都是同步锁。
程序中两个线程执行,
一个线程的线程执行体是DeadLock
类的run()
方法,
另一个线程的线程执行体是DeadLock
的init()
方法(主线程调用了init()
方法)。
其中run()
方法中让B对象调用firstB()
方法,
而init()
方法让A对象调用firstA()
方法。
发生死锁的过程分析
- 从本次运行结果来看,
init()
方法先执行,调用了A对象的firstA
方法,进入firstA方法之前,该线程会对A对象加锁,不过当当程序执行到firstA方法中的①号代码时,主线程睡眠200毫秒,在睡眠期间,主线程继续持有A对象的锁。 - 这时候
CPU
切换到执行另一个线程,所以看到副线程开始执行B实例的firstB
方法,进入firstB方法之前,该线程会对B对象加锁,不过当程序执行到firstB方法中的②号代码时,副线程也睡眠200毫秒,在睡眠期间,副线程继续持有B对象的锁。 - 接下来主线程会先醒过来,继续向下执行,执行到③号代码处时,要调用B对象的last()方法,但执行该方法之前必须先对B对象加锁,由于此时副线程正保持着B对象的锁,所以主线程无法加锁,主线程阻塞;
- 接下来副线程醒过来,继续向下执行,执行到④号代码处时,要调用A对象的 last方法,但执行该方法之前必须先对A对象加锁,由于主线程还没有释放A对象的锁,副线程也阻塞。
- 这就出现了主线程保持着A对象的锁,等待对B对象加锁,而副线程保持着B对象的锁,等待对A对象加锁,两个线程互相等待对方先释放,所以就出现了死锁。
由于Thread
类的suspend
方法也很容易导致死锁,所以Java
不再推荐使用该方法来暂停线程的执行
如何写一个死锁
- 先要有两个线程,设为主线程,副线程
- 要有两个类,设为A类对象和B类对象
- A类对象有两个同步方法,
- A类对象的第一个方法(设置firstA)的参数是B类对象,执行该方法时,先睡眠一会,然后调用B类对象的第二个无参同步方法lastB.
- A类对象的第二个同步方法lastB是个无参方法。
- B类对象有两个同步方法,
- B类对象的第一个方法firstB的参数传入A类对象,firstB方法中调用A类对象的第二个无参同步方法lastA.
- B类对象的第二个方法同样是无参方法。
- A类对象有两个同步方法,
- 创建A类对象,创建B类对象.
- 主线程的执行体中调用A类对象的第一个方法firstA,该方法传入B类对象作为参数.
- 副线程的执行题中条用B类对象的第一个方法firstB,该方法传入A类对象作为参数.
- 启动这两个线程,就会发生死锁.
原文链接: 16.5.6 死锁