01-死锁的概念

        死锁是指两个或两个以上的线程在执行过程中,因抢夺资源而造成的一种互相等待的现象,若无外力干涉,则它们无法再继续推进下去

02-产生原因

  • 系统资源不足
  • 进程运行推进顺序不合适
  • 系统资源分配不当

03-常见死锁场景与示例

3.1 嵌套锁顺序不一致

public class DeadLockDemo {
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (a){
                System.out.println("t1线程持有a锁,试图获取b锁");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b){
                    System.out.println("t1线程获取到b锁");
                }
            }
        },"t1").start();

        new Thread(() -> {
            synchronized (b){
                System.out.println("t2线程持有a锁,试图获取a锁");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println("t2线程获取到a锁");
                }
            }
        },"t2").start();
    }
}

分析:

  • t1线程执行:先获取a锁,再请求b锁
  • t2线程执行:先获取b锁,再请求a锁
  • 可能形成循环等待

3.2 动态锁顺序死锁

public void transfer(Account from, Account to, int amount) {
    synchronized (from) {
        synchronized (to) {
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
}

分析:

如果两个线程同时调用transfer(),但参数顺序相反:

  • 线程A:transfer(account1, account2, 100)
  • 线程B:transfer(account2, account1, 200)

可能产生死锁

3.3 资源死锁(如线程池任务相互等待)

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> future1 = executor.submit(() -> {
    Future<?> future2 = executor.submit(() -> System.out.println("Task2"));
    future2.get(); // 等待任务2完成
});
future1.get(); // 等待任务1完成

分析:

  • 线程池只有两个线程
  • 任务1提交任务2并等待任务2完成,任务2等待线程池空闲
  • 若任务2无法执行,任务1也无法完成,形成死锁

04-如何避免死锁

4.1 固定锁顺序

始终按全局一致顺序获取锁

public void transfer(Account a, Account b, int amount) {
    Object firstLock = a.id < b.id ? a : b;
    Object secondLock = a.id < b.id ? b : a;
    synchronized (firstLock) {
        synchronized (secondLock) {
            // 操作
        }
    }
}

4.2 使用超时机制

用tryLock()替代synchronized,设置超时时间

if (lock1.tryLock(100, TimeUnit.MILLISECONDS)) {
    try {
        if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
            try {
                // 操作
            } finally {
                lock2.unlock();
            }
        }
    } finally {
        lock1.unlock();
    }
}

4.3 避免嵌套锁

尽量只持有一个锁,或 将多个锁封装为一个大锁

05-如何排查死锁

5.1 纯命令

  • jps -l —> 相当于 java ps -ef -l —> 查看本地系统中所有正在运行的 Java 进程
  • jstack 进程编号 —> 查看进程堆栈信息

5.2 图形化

jconsole —> win + R 输入 jconsole,连接对应的Java进程,点击线程,点击检测死锁 即可查看

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐