https://blog.csdn.net/xidiancoder/article/details/56049315
产生死锁的原因:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
锁是一个非常有用的工具,运用场景非常多,因为他使用起来非常简单,而且易于理解。但同时它也带来一些困扰没那就是可能会引起死锁,一旦产生死锁,就会造成系统功能不可用。让我们先来看一段代码,这段代码会引起死锁,使线程t1和线程t2互相等待对方释放锁。
public class DeadLockDemo {
private static String A="A";
private static String B="B";
public static void main(String[] args){
new Main().deadLock();
}
private void deadLock(){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized(A){
try{
Thread.currentThread().sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized(B){
System.out.println("1");
}
}
}
});
Thread t2= new Thread(new Runnable() {
@Override
public void run() {
synchronized(B){
synchronized(A){
System.out.println("2");
}
}
}
});
t1.start();
t2.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
jvisualvm 查看Dump线程
在JDK 的 bin 目录下可以找到, jvisualvm.exe(Windows) jvisualvm(OSX/Linux)。如果你已经将Java环境加入到了系统环境当中的话,可以直接在命令行下,输入jvisualvm运行。
双击启动 Java VisualVM 后可以看到窗口左侧 “应用程序 ”栏中有“ 本地 ”、“远程 ” 、“快照 ”三个项目。
“本地 ”下显示的是在 localhost 运行的 Java 程序的资源占用情况,如果本地有 Java 程序在运行的话启动 Java VisualVM 即可看到相应的程序名,点击程序名打开相应的资源监控菜单,以图形的形式列出程序所占用的 CPU 、 Heap 、 PermGen 、类、线程的 统计信息。
“远程” 项下列出的远程主机上的 Java 程序的资源占用情况,但需要在远程主机上运行 jstatd 守护程序
从上图可知,检测到死锁,可以通过【线程Dump】查看具体的死锁线程的信息。
Found one Java-level deadlock: //发现死锁线程
=============================
"Thread-1":
waiting to lock monitor 0x00007f9ded0208b8 (object 0x000000076f598560, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f9ded021ca8 (object 0x000000076f598578, a java.lang.String),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at Main$2.run(Main.java:36)
- waiting to lock <0x000000076f598560> (a java.lang.String)
- locked <0x000000076f598578> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at Main$1.run(Main.java:25)
- waiting to lock <0x000000076f598578> (a java.lang.String)
- locked <0x000000076f598560> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
可以看到
Main.Java 25行:Thread-0房钱锁定了<0x000000076f598560>,并且在等待锁定<0x000000076f598578>
Main.Java 36行:Thread-1当前锁定了<0x000000076f598578>,并且在等待锁定<0x000000076f598560>
两个进程互相都需要对方线程释放其所占有资源(java.lang.String),而根据【请求与保持条件】原则,显然不会释放已经到手的资源,可以说是吃着碗里的,看着锅里的,两边都想占有。
如何避免死锁?
- 避免一个线程同时获取多个锁
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
- 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。