什么是死锁
死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期的阻塞,因此程序不可能正常终止。(两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。)
死锁的产生条件
- 互斥使用:即当资源被一个线程占用(使用)时,别的线程不能使用。
- 不可抢占:资源请求者不能强制从资源占有者手中抢夺资源,资源只能等待占有者主动释放。
- 请求和保持:即当资源请求者在请求其他资源的同时保持对原有资源的占有。
- 循环等待:存在一个等待队列,P1占有p2的资源,p2占有p3的资源,p3占有p1的资源。就这样形成了一个等待环路。
当上述四个条件都成立时,则形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
死锁的Java实例:
public class DeadLock {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args) {
LockA lockA = new LockA();
new Thread(lockA).start();
LockB lockB = new LockB();
new Thread(lockB).start();
}
static class LockA implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " : LockA 开始执行");
while (true) {
synchronized (obj1) {
System.out.println(new Date().toString() + " : LockA 锁住了obj1");
Thread.sleep(3000);// 获取obj1后先等一会儿,让LockA有足够的时间锁住obj2
synchronized (obj2) {
System.out.println(new Date().toString() + " : LockA 锁住了obj2");
Thread.sleep(60 * 1000);// 为测试,占用了就不放
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class LockB implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " : LockB 开始执行");
while (true) {
synchronized (obj2) {
System.out.println(new Date().toString() + " : LockB 锁住了obj2");
Thread.sleep(3000);// 获取obj2后先等一会儿,让LockB有足够的时间锁住obj1
synchronized (obj1) {
System.out.println(new Date().toString() + " : LockB 锁住了obj1");
Thread.sleep(60 * 1000);// 为测试,占用了就不放
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//结果打印
Wed Nov 01 17:38:00 CST 2017 : LockB 开始执行
Wed Nov 01 17:38:00 CST 2017 : LockB 锁住了obj2
Wed Nov 01 17:38:00 CST 2017 : LockA 开始执行
Wed Nov 01 17:38:00 CST 2017 : LockA 锁住了obj1
信号量(解决死锁)
为了解决这个问题,我们不使用显示的去锁,我们用信号量去控制。
信号量可以控制资源能被多少线程访问,这里我们指定只能被一个线程访问,就做到了类似锁住。而信号量可以指定去获取的超时时间,我们可以根据这个超时时间,去做一个额外处理。
对于无法成功获取的情况,一般就是重复尝试,或指定尝试的次数,也可以马上退出。
详细解决方案参考:Java死锁和解决方式