死锁
概念
当线程 Thread-0 持有锁 Lock1,Thread-1 持有锁 Lock2, 此时 Thread-0 申请 Lock2 锁的使用权, Thread-1 申请 Lock1 锁的使用权, Thread-0 和 Thread-1 都在无限地等待锁的使用权. 这样就造成了死锁.
死锁是主要由于设计的问题. 一旦出现死锁, 死锁的线程就会永远不能使用, 同步方法不会被执行, 死锁线程不会被自动终止, 无尽地消耗 CPU 资源.
例子
看一个例子
ThreadDomain29 类, 模拟图片中, 线程持有一个锁, 申请被其他线程持有的锁的情况
- public class ThreadDomain29 {
- private final Object obj1 = new Object();
- private final Object obj2 = new Object();
- public void obj1obj2() throws Exception
- {
- synchronized (obj1)
- {
- Thread.sleep(2000);
- synchronized (obj2)
- {
- System.out.println("obj1obj2 end!");
- }
- }
- }
- public void obj2obj1() throws Exception
- {
- synchronized (obj2)
- {
- Thread.sleep(2000);
- synchronized (obj1)
- {
- System.out.println("obj2obj1 end!");
- }
- }
- }
- }
MyThread29_0 类
- public class MyThread29_0 extends Thread{
- private ThreadDomain29 dl;
- public MyThread29_0(ThreadDomain29 dl)
- {
- this.dl = dl;
- }
- public void run()
- {
- try
- {
- dl.obj1obj2();
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
- }
MyThread29_1 类
- public class MyThread29_1 extends Thread{
- private ThreadDomain29 dl;
- public MyThread29_1(ThreadDomain29 dl)
- {
- this.dl = dl;
- }
- public void run()
- {
- try
- {
- dl.obj2obj1();
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- }
- }
main 方法
- public class MyThread29_main {
- public static void main(String[] args)
- {
- ThreadDomain29 dl = new ThreadDomain29();
- MyThread29_0 t0 = new MyThread29_0(dl);
- MyThread29_1 t1 = new MyThread29_1(dl);
- t0.start();
- t1.start();
- while(true);
- }
- }
因为发生了死锁, 所以你不会看到任何结果.
死锁排查
jps+jstack
jps 找到进程 id
jstack 打印堆栈
输入 jstack 10208
可以看到, 找到了两个死锁.
JConsole
命令行输入 JConsole
点击 "检测死锁"
已经检测到了死锁
Java Visual VM
命令行输入 jvisualvm
找到我们的进程
自动检测到死锁, 推荐使用这种方式
死锁避免
1. 尽量少用嵌套的锁.
2. 如果一定要用嵌套锁, 那么请规定好获取锁的顺序. 例子如下:
- // 伪代码
- //condition1 可以是属性值大小, hash 值大小的比较等等
- if(condition1){
- synchronized (obj2)
- {
- Thread.sleep(2000);
- synchronized (obj1)
- {
- System.out.println("obj2obj1 end!");
- }
- }
- }else{
- synchronized (obj1)
- {
- Thread.sleep(2000);
- synchronized (obj2)
- {
- System.out.println("obj1obj2 end!");
- }
- }
- }
3. 使用 Lock 的 tryLock 方法, 它表示用来尝试获取锁, 如果获取成功, 则返回 true, 如果获取失败 (即锁已被其他线程获取), 则返回 false, 这个方法无论如何都会立即返回. 在拿不到锁时不会一直在那等待.
后面讲到 ReentrantLock 会详细分析.
来源: https://www.cnblogs.com/Java-Starter/p/11152705.html