问:什么是线程死锁?
答案很简单,当有两个或更多的线程在等待对方释放锁并无限期地卡住时,这种情况就称为死锁。
问:死锁产生的必要条件?
(1) 互斥:一次只有一个进程可以使用一个资源。其他进程不能访问已分配给其他进程的资源。
(2)占有且等待:当一个进程在等待分配得到其他资源时,其继续占有已分配得到的资源。
(3)非抢占:不能强行抢占进程中已占有的资源。
(4)循环等待:存在一个封闭的进程链,使得每个资源至少占有此链中下一个进程所需要的一个资源。
问:设计一个死锁程序?
代码如下,方法method1中,首先是获取String对象,然后是Integer对象;方法method2中,首先是获取Integer对象,然后是String对象。由于两个方法获取锁的顺序是相反的,可能method1方法在获取了String对象锁时,然后去获取Integer锁,发现Integer锁被method2获取了,这时候就造成了死锁。
public class DeadLockDemo {
/*
* This method request two locks, first String and then Integer
**/
public void method1() {
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
synchronized (Integer.class) {
System.out.println("Aquired lock on Integer.class object");
}
}
}
public void method2() {
synchronized (Integer.class) {
System.out.println("Aquired lock on Integer.class object");
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
}
}
}
}
问:如何解决死锁?
答:避免死锁的三种方式:
1、设置加锁顺序
2、设置加锁时限
3、死锁检测
1、设置加锁顺序
线程按照相同的顺序获取锁对象。
public class DeadLockDemo {
/*
* This method request two locks, first String and then Integer
**/
public void method1() {
synchronized (String.class) {
System.out.println("Aquired lock on String.class object");
synchronized (Integer.class) {
System.out.println("Aquired lock on Integer.class object");
}
}
}
public void method2() {
synchronized (String.class) {
System.out.println("Aquired lock on Integer.class object");
synchronized (Integer.class) {
System.out.println("Aquired lock on String.class object");
}
}
}
}
缺点:
按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁,并知道他们之间获取锁的顺序是什么样的。
2、设置加锁时限:(超时重试)
若一个线程在一定的时间里没有成功的获取到锁,则会进行回退并释放之前获取到的锁,然后等待一段时间后进行重试。
缺点:
但是由于存在锁的超时,通过设置时限并不能确定出现了死锁,每种方法总是有缺陷的。有时为了执行某个任务。某个线程花了很长的时间去执行任务,如果在其他线程看来,可能这个时间已经超过了等待的时限,可能出现了死锁。
3、死锁检测:
当一个线程获取锁的时候,会在相应的数据结构中记录下来,相同下,如果有线程请求锁,也会在相应的结构中记录下来。当一个线程请求失败时,需要遍历一下这个数据结构检查是否有死锁产生。