跳转到内容

Java死锁

来自代码酷

Java死锁[编辑 | 编辑源代码]

死锁(Deadlock)是多线程编程中的一种常见问题,指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,导致这些线程都无法继续执行下去。死锁通常发生在多个线程持有部分资源并等待其他线程释放资源时,形成一个循环等待链。

死锁的必要条件[编辑 | 编辑源代码]

死锁的发生需要满足以下四个必要条件(由计算机科学家 Edsger Dijkstra 提出):

  1. 互斥条件:资源一次只能由一个线程占用。
  2. 占有并等待:线程持有至少一个资源,并等待获取其他被占用的资源。
  3. 非抢占条件:线程已持有的资源不能被其他线程强行抢占,只能由线程自己释放。
  4. 循环等待条件:存在一个线程的循环等待链,每个线程都在等待下一个线程所占用的资源。

如果上述任一条件被破坏,死锁就不会发生。

死锁示例[编辑 | 编辑源代码]

以下是一个典型的Java死锁示例,其中两个线程互相等待对方释放锁:

public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

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

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: Holding lock 2...");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("Thread 2: Waiting for lock 1...");
                synchronized (lock1) {
                    System.out.println("Thread 2: Acquired lock 1!");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

输出分析[编辑 | 编辑源代码]

程序运行后,可能会输出以下内容,然后卡住(因为死锁):

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

此时,线程1持有`lock1`并等待`lock2`,而线程2持有`lock2`并等待`lock1`,导致两个线程都无法继续执行。

死锁的检测与解决[编辑 | 编辑源代码]

检测死锁[编辑 | 编辑源代码]

在Java中,可以使用工具检测死锁:

  • 使用`jstack`命令查看线程转储(Thread Dump)。
  • 使用`ThreadMXBean`编程检测死锁:
import java.lang.management.ThreadMXBean;
import java.lang.management.ManagementFactory;

public class DeadlockDetector {
    public static void main(String[] args) {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean();
        long[] threadIds = bean.findDeadlockedThreads();
        if (threadIds != null) {
            System.out.println("Deadlock detected!");
        }
    }
}

解决死锁的方法[编辑 | 编辑源代码]

1. 避免嵌套锁:尽量减少锁的嵌套使用。 2. 锁顺序化:确保所有线程以相同的顺序获取锁。 3. 超时机制:使用`tryLock()`设置超时时间,避免无限等待。 4. 死锁恢复:通过中断或终止线程打破死锁。

实际案例[编辑 | 编辑源代码]

在数据库系统中,死锁经常发生在事务处理中。例如:

  • 事务A锁定行1,并尝试锁定行2。
  • 事务B锁定行2,并尝试锁定行1。

数据库系统通常会自动检测并回滚其中一个事务以解决死锁。

死锁的可视化[编辑 | 编辑源代码]

以下是一个死锁循环等待的示意图:

graph LR Thread1 -->|等待 lock2| Thread2 Thread2 -->|等待 lock1| Thread1

数学表示[编辑 | 编辑源代码]

死锁可以用资源分配图(Resource Allocation Graph)表示。如果图中存在环,则可能发生死锁。数学上可以表示为: 死锁G 其中G是资源分配图。

总结[编辑 | 编辑源代码]

死锁是多线程编程中的一个重要问题,理解其成因和解决方法对于编写健壮的并发程序至关重要。通过合理设计锁策略和使用工具检测死锁,可以有效避免或解决死锁问题。