如何避免Java中的死锁?
这是多线程问题之一,在高级问题上有更多问题,并且有很多后续问题。即使问题看起来非常基本,但一旦你开始深入,大多都会陷入困境。
面试问题1 :
始于“什么是死锁?”
答:答案很简单,当两个或多个线程正在等待释放锁并在无限时间内卡住时,情况称为死锁。它只会在多任务处理的情况下发生。
面试问题2:
你如何检测Java中的死锁?
答:虽然这可能有很多答案,但我的版本是第一个我会查看代码,如果我看到嵌套的synchronized块或从其他人调用一个synchronized方法或试图锁定不同的对象然后如果不是很小心很有可能死锁。
其他方法是在运行应用程序时实际锁定时找到它,打印应用程序日志文件中所有线程的状态可以看到哪个线程锁定在哪个对象上。
其他方法是使用jconsole,它将准确显示哪些线程被锁定以及哪个对象。
面试问题3:
编写一个会导致死锁的Java程序?
答:
/ **
* Java程序通过强制循环等待来创建死锁。
*
* /
public DeadLockDemo {
/ *
*此方法请求两个锁,首先是String,然后是Integer
* /
public void method1(){
synchronized(String.class){
System.out.println(“对String.class对象的获取锁定”);
synchronized(Integer.class){
System.out.println(“对Integer.class对象的获取锁定”);
}
}
}
/ *
*此方法也请求相同的两个锁,但确切地说
*相反的顺序,即第一个整数,然后是字符串。
*如果一个线程持有String锁,则会产生潜在的死锁
*和其他持有整数锁定,他们永远等待对方。
* /
public void method2(){
synchronized(Integer.class){
System.out.println(“对Integer.class对象的获取锁定”);
synchronized(String.class){
System.out.println(“对String.class对象的获取锁定”);
}
}
}
}
如果两个或多个线程都调用method1()和method2(),则很可能发生死锁,因为如果线程1在执行method1()时获取Sting对象的锁定,并且线程2在执行method2时获取对Integer对象的锁定()两者将等待彼此释放对Integer和String的锁定以继续进行,这将永远不会发生。这就发生了死锁 都在等待对方释放对象的锁。
面试问题4:
如何避免Java中的死锁?
如果仔细查看了上面的代码,那么可能已经发现死锁的真正原因不是多线程,而是它们请求锁定的方式,如果提供有序访问然后问题将得到解决,这里是我的固定版本,这避免了通过避免循环等待而没有先发制人的死锁。
public class DeadLockFixed {
/ **
*两种方法现在都以相同的顺序请求锁定,首先是Integer,然后是String。
*你也可以做反向,例如先是String然后是Integer,
*两者都会解决问题,只要两种方法都请求锁定
*按照一致的顺序。
* /
public void method1(){
synchronized(Integer.class){
System.out.println(“对Integer.class对象的获取锁定”);
synchronized(String.class){
System.out.println(“对String.class对象的获取锁定”);
}
}
}
public void method2(){
synchronized(Integer.class){
System.out.println(“对Integer.class对象的获取锁定”);
synchronized(String.class){
System.out.println(“对String.class对象的获取锁定”);
}
}
}
}
现在不存在任何死锁,因为两个方法都以相同的顺序访问Integer和String类文字的锁。因此,如果线程A获取对Integer对象的锁定,则线程B将不会继续,直到线程A释放整数锁定,同样,即使线程B保持字符串锁定,线程A也不会被阻止,因为现在线程B不会期望线程A释放整数锁定继续前进。