目录
1.死锁定义
1.1.代码演示
1.2.使用jconsole/jvisualvm/jmc查看死锁
①使用jconsole:最简单。
②使用jvisualvm:(Java虚拟机)更方便,更直观,更智能,更高级,是合适的选择。
③使用jmc:加载的资源更多,需要的时间更长,最高级。
2.死锁产生原因
①互斥条件
②不可被剥夺条件
③请求并持有条件
④环路等待条件
3.如何解决死锁问题?
解决死锁方案1:破环请求并持有条件
解决死锁方案2:破坏环路等待条件/使用顺序锁
1.死锁定义
死锁是指两个或两个以上的执行单元(进程/线程/协程(Java中没有协程的概念))在执⾏过程中,由于竞争资源或者由于彼此通信⽽造成的⼀种阻塞的现象,若⽆外⼒作⽤,它们都将⽆法推进下去。
也就是两个进程/线程/协程拥有锁的情况下,⼜在尝试获取对⽅锁,从⽽造成程序⼀直阻塞的情况。
一个线程可以拥有多把锁,但一把锁同一时间只能被一个线程所占用。
1.1.代码演示
import java.util.concurrent.TimeUnit;
/**
* 死锁示例
*/
public class Thread18 {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread t1 = new Thread(() -> {
//1.占有一把锁(锁A)
synchronized (lockA){
System.out.println("线程1:获得锁A");
//因为线程的执行是不可控的。中间设置空档期,休眠1s,让线程2有时间先占有锁B(线程1得到锁B的速度不能大于线程2得到锁B的速度)
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//2.尝试获取线程2的锁B
synchronized (lockB){
System.out.println("线程1:获得锁B");
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
//1.占有一把锁(锁B)
synchronized (lockB){
System.out.println("线程2:获得锁B");
//休眠1s(保证线程1有充足的时间得到锁A)
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//2.尝试获取线程1的锁A
synchronized (lockA){
System.out.println("线程2:获得锁A");
}
}
});
t2.start();
}
}
1.2.使用jconsole/jvisualvm/jmc查看死锁
①使用jconsole:最简单。
②使用jvisualvm:(Java虚拟机)更方便,更直观,更智能,更高级,是合适的选择。
都在同一级目录。
③使用jmc:加载的资源更多,需要的时间更长,最高级。
2.死锁产生原因
①互斥条件
⼀个资源(锁)同一时间只能被⼀个线程占有,当这个资源被占⽤之后其他线程就只能等待。
②不可被剥夺条件
当⼀个线程不主动释放资源时,此资源⼀直被拥有线程占有。
③请求并持有条件
线程已经拥有了⼀个资源之后,又尝试请求新的资源。
④环路等待条件
产⽣死锁⼀定是发⽣了线程资源环形链。
这四个因素(缺一不可)共同导致了死锁。
3.如何解决死锁问题?
改变死锁中的任意⼀个或多个条件就可以解决死锁的问题,其中可以被修改的条件只有后两个:请求并持有条件和环路等待条件(人为控制,可被打破,可被修改);前两个条件都是系统设定的。
解决死锁方案1:破环请求并持有条件
import java.util.concurrent.TimeUnit;
/**
* 解决死锁方案:破环请求并持有条件
*/
public class UnDeadLock1 {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread t1 = new Thread(() ->{
synchronized (lockA){
System.out.println("线程1:得到了锁A");
//业务代码...
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
// synchronized (lockB){
// System.out.println("线程1:得到了锁B");
// //业务代码...
// System.out.println("线程1:释放了锁B");
// }
System.out.println("线程1:释放了锁A");
}
},"线程1");
t1.start();
Thread t2 = new Thread(() ->{
synchronized (lockB){
System.out.println("线程2:得到了锁B");
//业务代码...
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
// synchronized (lockA){
// System.out.println("线程2:得到了锁A");
// //业务代码...
// System.out.println("线程2:释放了锁A");
// }
System.out.println("线程2:释放了锁B");
}
},"线程2");
t2.start();
}
}
解决死锁方案2:破坏环路等待条件/使用顺序锁
import java.util.concurrent.TimeUnit;
/**
* 解决死锁方案:破坏环路等待条件
*/
public class UnDeadLock2 {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
Thread t1 = new Thread(() ->{
synchronized (lockA){
System.out.println("线程1:得到了锁A");
//业务代码...
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
synchronized (lockB){
System.out.println("线程1:得到了锁B");
//业务代码...
System.out.println("线程1:释放了锁B");
}
System.out.println("线程1:释放了锁A");
}
},"线程1");
t1.start();
Thread t2 = new Thread(() ->{
synchronized (lockA){
System.out.println("线程2:得到了锁A");
//业务代码...
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
synchronized (lockB){
System.out.println("线程2:得到了锁B");
//业务代码...
System.out.println("线程2:释放了锁B");
}
System.out.println("线程2:释放了锁A");
}
},"线程2");
t2.start();
}
}