在多线程的环境下,会对资源进行抢夺。当两个线程锁住了当前资源,但都需要对方的资源才能进行下一步操作,这个时候两方就会一直等待对方的资源释放。这就形成了死锁。这些永远在互相等待的进程称为死锁进程。
死锁产生的条件:
1、互斥:资源的锁是排他性的,加锁期间只能有一个线程拥有该资源。其他线程只能等待锁释放才能尝试获取该资源。
2、请求和保持:当前线程已经拥有至少一个资源,但其同时又发出新的资源请求,而被请求的资源被其他线程拥有。此时进入保持当前资源并等待下个资源的状态。
3、不剥夺:线程已拥有的资源,只能由自己释放,不能被其他线程剥夺。
4、循环等待:是指有多个线程互相的请求对方的资源,但同时拥有对方下一步所需的资源。形成一种循环,类似2)请求和保持。但此处指多个线程的关系。并不是指单个线程一直在循环中等待。
public class DeadLock implements Runnable {
public static int flag = 1;
//static 变量是 类对象共享的
static Object o1 = new Object();
static Object o2 = new Object();
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":此时flag = " + flag);
if (flag == 1){
synchronized (o1){
try{
System.out.println("我是" + Thread.currentThread().getName() + "锁住o1");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "醒来->准备获取 o2");
}catch (Exception e){
e.printStackTrace();
}
synchronized (o2){
System.out.println(Thread.currentThread().getName() + "拿到 o2");
}
}
}
if (flag == 0){
synchronized (o2){
try {
System.out.println("我是" + Thread.currentThread().getName() + "锁住 o2");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "醒来->准备获取 o1");
}catch (Exception e){
e.printStackTrace();
}
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "拿到 o1");
}
}
}
}
public static void main(String[] args){
DeadLock t1 = new DeadLock();
DeadLock t2 = new DeadLock();
t1.flag = 1;
new Thread(t1).start();
//让main线程休眠1秒钟,保证t2开启锁住o2.进入死锁
try{
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
t2.flag = 0;
new Thread(t2).start();
}
}
运行结果
Thread-0:此时flag = 1
我是Thread-0锁住o1
Thread-1:此时flag = 0
我是Thread-1锁住 o2
Thread-0醒来->准备获取 o2
Thread-1醒来->准备获取 o1
t1创建,t1先拿到o1的锁,开始休眠3秒。然后 t2线程创建,t2拿到o2的锁,开始休眠3秒。然后 t1先醒来,准备拿o2的锁,发现o2已经加锁,只能等待o2的锁释放。 t2后醒来,准备拿o1的锁,发现o1已经加锁,只能等待o1的锁释放。 t1,t2形成死锁
解决办法:
1、(互斥)尽量少用互斥锁,能加读锁,不加写锁。当然这条无法避免。
2、(请求和保持)采用资源静态分配策略(进程资源静态分配方式是指一个进程在建立时就分配了它需要的全部资源).我们尽量不让线程同时去请求多个锁,或者在拥有一个锁又请求不到下个锁时,不保持等待,先释放资源等待一段时间在重新请求。
3、(不剥夺)允许进程剥夺使用其他进程占有的资源。优先级。
4、(循环等待)尽量调整获得锁的顺序,不发生嵌套资源请求。加入超时。