先来看一下死锁的官方概念:集合中的每一个进程都在等待只能由本集合中的其他进程才能引发的事件,那么该组进程是死锁的。
读这个概念,感觉有一种似懂非懂的感觉,但本意就是锁住了,谁也用不了。死锁一般发生在两个及更多线程的执行过程中,由于共同竞争使用某一个资源,而造成的一种阻塞状态。举一个生活中的例子,比如有一个独木桥,A和B同时要过这个独木桥,当两者都同时都在桥上(假如不能后退),谁也过不了桥,也就是说独木桥这个资源,只能被A或者B占用,如果都想占用,那么就会出现谁都过不去状态。
对于死锁来说,其实就是一个进程中的多个个线程都在等待被其他线程占用并堵塞了的资源。
下面看一下具体代码:
public class DeadLock implements Runnable {
final String S1 = "S1";
final String S2 = "S2";
String user;
public DeadLock(String user) {
this.user = user;
}
@Override
public void run() {
if (user.equals("A")) {
synchronized (S1) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A lock " + S1);
synchronized (S2) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A lock " + S2);
}
}
} else if (user.equals("B")) {
synchronized (S2) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B lock " + S2);
synchronized (S1) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B lock " + S1);
}
}
}
}
}
然后在Main中执行:
DeadLock lock1 = new DeadLock("A");
new Thread(lock1).start();
DeadLock lock2 = new DeadLock("B");
new Thread(lock2).start();
运行后的结果是:
A lock S1
B lock S2
分析一下为什么会是这样,假如A先执行,B再执行(因为是线程,也可能是B先执行,B先执行,其结果也是一样的),A执行先把S1锁住,两秒执行完后,去执行S2时,发现S2正在被B执行,只能等待,当然这个过程并没有释放掉S1。当B执行完S2后,去执行S1时发现它正在被A持有,所以只能等待,现在两个线程共同在等待对方释放掉需要的资源,所以就出现了死锁状态。结果就是A永远无法使用S2,B永远无法使用S1,程序也无法退出,这就是一个典型的死锁。
要避免死锁,其实跟设计有关,基本考虑以下几方面就可以避免:
- 不要出现这种一下占用多个资源的情况,就如上面的代码。
- 另外一种是使用Lock去检查锁,这个还没有深研究。
- 让线程顺序执行,例如上面的例子,让A线程先执行完,B再去执行。