1. 什么是死锁?

死锁是一种特定的程序状态,主要是由于循环依赖导致彼此一直处于等待中,而使得程序陷入僵局,相当尴尬。死锁不仅仅发生在线程之间,而对于资源独占的进程之间同样可能出现死锁。通常来说,我们所说的死锁,是指两个或多个线程之间,由于互相持有对方所需要的锁,进而产生永久阻塞的情况。举个栗子,线程1手里有锁A,它想要获得锁B,与此同时,线程2手里有锁B,它想要获得锁A,相持不下,那么这两个线程将永远地等待下去。

一句话,计算机资源有限,大家互相占着资源而不放手,而且还想要更多,导致计算机say no.

java 内部类产生死锁 java的死锁_java

2. 定位死锁

最常见的方式是利用jstack等工具获取线程,然后定位互相之间的依赖关系,然后找到死锁。如果是比较明显的死锁,是可以直接定位到了,类似JConsole,甚至于通过图形界面进行有限的死锁检测。

下面这段代码执行后就会产生死锁

/**
 * synchronized 死锁
 * 有两个线程A和B,线程A先执行m1方法,在m1方法中停顿5s,当启动线程A 2s 后启动线程B
 * 线程B执行m2方法
 * 此时就形成了死锁 A线程需要锁 lockb , B线程需要locka
 */
class SynchronizedDeadLockDemo {
    private Object locka = new Object();
    private Object lockb = new Object();
    public void dead1() throws InterruptedException {
        synchronized (locka) {
            System.out.println(Thread.currentThread().getName() + "\t[占有锁locka]");
            TimeUnit.SECONDS.sleep(5);
            synchronized (lockb) {
                System.out.println(Thread.currentThread().getName() + "\t[占有锁lockb]");
            }
            System.out.println(Thread.currentThread().getName() + "\t[释放锁lockb]");
        }
        System.out.println(Thread.currentThread().getName() + "\t[释放锁locka]");
    }
    public void dead2() {
        synchronized (lockb) {
            System.out.println(Thread.currentThread().getName() + "\t[占有锁lockb]");
            synchronized (locka) {
                System.out.println(Thread.currentThread().getName() + "\t[占有锁locka]");
            }
            System.out.println(Thread.currentThread().getName() + "\t[释放锁locka]");
        }
        System.out.println(Thread.currentThread().getName() + "\t[释放锁lockb]");
    }
}

/**
 * @author shenguangyang
 */
public class Main {
    public static void main(String[] args) throws InterruptedException {
        SynchronizedDeadLockDemo lockDemo = new SynchronizedDeadLockDemo();
        new Thread(() -> {
            try {
                lockDemo.dead1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        TimeUnit.SECONDS.sleep(2);
        new Thread(() -> {
            lockDemo.dead2();
        },"B").start();
    }
}

java 内部类产生死锁 java的死锁_java 内部类产生死锁_02

2.1. 使用 jps + jstack

第一:在windons命令窗口,使用 jps -l 【不会使用jps请自行查询资料】

java 内部类产生死锁 java的死锁_java_03

第二:使用 jstack -l 7068 【不会使用jstack请自行查询资料】

java 内部类产生死锁 java的死锁_java 内部类产生死锁_04

2.2. 使用jconsole

在window打开 JConsole,JConsole是一个图形化的监控工具!

在windons命令窗口 ,输出 JConsole

java 内部类产生死锁 java的死锁_java_05

java 内部类产生死锁 java的死锁_java_06

java 内部类产生死锁 java的死锁_java 内部类产生死锁_07

2.3. 使用Java Visual VM

在window打开 jvisualvm,jvisualvm是一个图形化的监控工具!

在windons命令窗口 ,输出 jvisualvm

java 内部类产生死锁 java的死锁_多线程_08

java 内部类产生死锁 java的死锁_死锁_09

3. 死锁的诞生

  1. 互斥条件 资源独占,若其他进程使用,必须等待;
  2. 长期持有 死锁一直存在,自己不会释放,也不能被释放;
  3. 循环依赖关系 彼此等待,形成进程循环等待环。

4. 如何防患?

  1. 尽量避免使用多个锁(如果有可能的话)。
  2. 规范的使用多个锁,并设计好锁的获取顺序。
  3. 随用随放。即是,手里有锁,如果还要获得别的锁,必须释放全部资源才能各取所需。
  4. 规范好循环等待条件。比如,使用超时循环等待,提高程序可控性