这里模拟的是两个线程共享同两个对象时,嵌套加锁产生死锁的问题,后面借助JDK提供的工具排查出现死锁的线程。

死锁案例

先创建两个类Obj1和Obj2,类体没有任何代码,只是为了创建两个对象而已。当然了,以下两个类不是必须的,也可以用两个字符串代替这两个类,只要能够让这两个线程共享同两个对象就行。例如,线程1和线程2同时共享学生1和学生2这两个对象。

public class Obj1 {
}
public class Obj2 {
}

接下来创建两个线程类,这里用的是实现Runnable接口的方式,也可以使用继承Thread类或实现Callable接口的方式。当这两个线程开始执行后,第一个线程会拿到Obj1对象的锁,由与线程是并行执行的,可能是第一个线程先执行,也可能是第二个线程先执行。为了确保每个线程只能拿到其中一个对象的锁,让这两个线程都睡眠1000毫秒,1000毫秒过去后两个线程各自拿到了其中一个对象的锁,再尝试去拿另一个对象锁的时候就只能相互等待了,如果程序没有停止运行的话会无限期等下去,这也就产生了死锁。

public class FirstThread implements Runnable {

    private Obj1 obj1;

    private Obj2 obj2;

    public FirstThread(Obj1 obj1, Obj2 obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj1) {
            System.out.println(Thread.currentThread().getName() + "拿到了:" + obj1 + "的锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj2) {
                System.out.println(Thread.currentThread().getName() + "拿到了:" + obj2 + "的锁");
            }
        }
    }
}
public class SecondThread implements Runnable {

    private Obj1 obj1;

    private Obj2 obj2;

    public SecondThread(Obj1 obj1, Obj2 obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj2) {
            System.out.println(Thread.currentThread().getName() + "拿到了:" + obj2 + "的锁");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj1) {
                System.out.println(Thread.currentThread().getName() + "拿到了:" + obj1 + "的锁");
            }
        }
    }
}

在外面提前创建好Obj1和Obj2对象,注意:Obj1和Obj2对象只new一次。然后在创建第一个线程和第二个线程的同时将Obj1和Obj2这两个被共享的对象作为形参传入即可实现共享。为了更直观查看结果可以给这两个线程取个名称,最后启动两个线程。

public class TestDeadlock {

    public static void main(String[] args) {
        Obj1 obj1 = new Obj1();
        Obj2 obj2 = new Obj2();
        Thread t1 = new Thread(new FirstThread(obj1,obj2));
        Thread t2 = new Thread(new SecondThread(obj1,obj2));
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}

看看效果,很明显,两个线程僵持住了。

Java 死锁分析log java死锁排查_Java 死锁分析log

 排查死锁

如果项目比较大,手写的线程比较多,不确定是哪个线程出问题时,可以借助JDK提供的客户端工具来查看。客户端工具在JDK安装目录下的bin文件夹下的jconsole.exe。

Java 死锁分析log java死锁排查_死锁_02

 这边是在本地测试的,所以选择本地进程,找到自己进程的名称点击连接。

Java 死锁分析log java死锁排查_开发语言_03

Java 死锁分析log java死锁排查_开发语言_04

  

Java 死锁分析log java死锁排查_java_05

 

 

Java 死锁分析log java死锁排查_Java 死锁分析log_06