Skip to content

06_死锁

两个或多个线程互相持有对方所需的资源,导致线程无法继续执行,称为死锁。死锁是多线程编程中常见的问题,也是非常难以调试和解决的问题。

1. 死锁的案例

java
/**
  * 死锁案例
 * t1.锁住lock1,等待lock2
 * t2.锁住lock2,等待lock1
  */
public class DeadLockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: Holding lock 1...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("Thread 1: Waiting for lock 2...");
                synchronized (lock2) {
                    System.out.println("Thread 1: Holding lock 1 & 2...");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock 2...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("Thread 2: Waiting for lock 1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Holding lock 1 & 2...");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

2. 产生死锁的原因

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

3. 避免死锁的方法

  • 避免一个线程同时获取多个锁
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代内部锁机制
  • 对于数据库锁,加锁和解锁必须在一个数据库事务中,否则会出现死锁
  • 要注意加锁顺序,保证每个线程按同样的加锁顺序.
  • 要注意加锁时限,针对锁设置一个加锁时限.
  • 要注意死锁检查,确保在第一时间发现死锁并解决.

4. 死锁的检测

  • 使用jps命令查看Java进程ID
  • 使用jstack 进程ID命令查看线程堆栈信息
  • 或者使用jconsolejvisualvm查看线程堆栈信息

5. 死锁的解决

  • 通过分析死锁的原因,找到导致死锁的根本原因
  • 通过调整线程的运行顺序,避免死锁的发生
  • 通过增加资源,避免死锁的发生
  • 通过减少资源的占用,避免死锁的发生
  • 通过增加超时时间,避免死锁的发生

终止某个线程

  • 终止某个线程是最简单的恢复死锁的方法,但是需要谨慎使用。当一个线程被终止时,可能会导致程序异常或数据不一致等问题。

回滚一定的步数

  • 回滚一定的步数是指,当发生死锁时,回滚已经获取的资源,然后重新获取资源。这种方法可以避免终止线程可能带来的问题,但是需要保证回滚和重新获取资源的操作是原子性的。