多线程死锁问题:
一、什么是死锁和原因?
死锁的四个条件:(互斥、不可剥夺、请求持有、循环等待)
最根本原因是:线程在获得一个锁L1的情况下再去申请另外一个锁L2,也就是锁L1想要包含了锁L2,也就是说在获得了锁L1,并且没有释放锁L1的情况下,又去申请获得锁L2,这个是产生死锁的最根本原因**。另一个原因是**默认的锁申请操作是阻塞的。
二、手写一个java多线程的死锁案例?
案例1:锁定类变量(静态变量)
package cetcocean;
/**
* @description:对两个类变量加锁,导致死锁
* @author: fangchangtan
* @create: 2019-04-03 15:10
* */
public class MultiThreadDeadLock implements Runnable {
private Object objects1 = new Object();
private Object objects2 = new Object();
private boolean flag;
public static void main(String[] args) {
Thread thread1 = new Thread(new MultiThreadDeadLock(true),"t1");
Thread thread2 = new Thread(new MultiThreadDeadLock(false),"t2");
thread1.start();
thread2.start();
}
public MultiThreadDeadLock(boolean flag ) {
this.flag =flag;
}
@Override
public void run() {
while (true) {
if (flag) {
synchronized (objects1) {
System.out.println("111" + Thread.currentThread().getName() +" lock objects1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objects2) {
System.out.println("222" + Thread.currentThread().getName() +" lock objects2");
}
}
} else {
synchronized (objects2) {
System.out.println("222" + Thread.currentThread().getName() +" lock objects2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objects1) {
System.out.println("111" + Thread.currentThread().getName() +" lock objects1");
}
}
}
}
}
}
运行结果如下:
内容解释:两个线程分别依次锁定两个类变量,相互挟持自身对象监视器【当前类的监视器】的所而不释放,导致循环等待对方线程释放所需对象的监视器,导致循环等待。最终陷入死循环;
案例2:锁定实例对象的监视器
package cetcocean;
/**
* @description:锁定两个实例变量的对象监视器,相互等待,导致死锁
* @author: fangchangtan
* @create: 2019-04-03 15:31
* */
public class TestDeadLock {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
myRunner myRunner1 = new myRunner(obj1, obj2);
myRunner myRunner2 = new myRunner(obj2, obj1);
new Thread(myRunner1,"t1").start();
new Thread(myRunner2,"t2").start();
}
}
class myRunner implements Runnable {
private Object obj1;
private Object obj2;
public myRunner(Object obj1,Object obj2) {
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
while (true) {
synchronized (obj1) {
System.out.println("111---" + Thread.currentThread().getName() +" ;lock obj: " +obj1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("222***" + Thread.currentThread().getName() +" ;lock obj: "+ obj2);
}
}
}
}
}
运行结果:
两个线程相互持有对方资源的对象监视器,互不释放,导致多线程循环等待对象释放,导致死锁!
三、避免死锁的方法:
在有些情况下死锁是可以避免的。下面介绍三种用于避免死锁的技术:
-
加锁顺序(线程按照一定的顺序加锁)
例如:设计时考虑清楚锁的顺序,尽量减少嵌在的加锁交互数量。
-
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
例如:既然死锁的产生是两个线程无限等待对方持有的锁,那么只要等待时间有个上限不就好了。当然synchronized不具备这个功能,但是我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后便会返回一个失败信息。
-
死锁检测