How to Avoid Deadlock in Java

17 Mar 2025 | 5 分钟阅读

在 Java 中,死锁多线程的一部分。多线程环境允许我们同时运行多个线程以实现多任务处理。有时线程会发现自己处于永远的等待状态,这就是死锁情况。

死锁是指两个或两个以上的线程试图访问被另一个线程获取的同一个对象。由于线程都在等待释放对象,因此这种情况被称为死锁。这种情况发生在两个以上的线程之间。“如何在 Java 中避免死锁”Java 面试中最重要和最受欢迎的问题。在本节中,我们将学习如何在 Java 中检测和避免死锁

Java 中的死锁是什么?

在线程中,每个对象都有一个锁。为了获取锁,Java 提供了同步机制来锁定方法或代码块。它允许一次只有一个线程可以访问该方法。

但是,如果一个线程想执行一个同步方法,它首先会尝试获取锁。如果另一个线程已经获取了该锁,那么(想要获取锁的)线程将不得不等待,直到前一个线程不释放锁。

让我们通过一个例子来理解。

示例

假设有两个线程 A 和 B。线程 A 和 B 分别获取了 Object-A 和 Object-B 的锁。假设线程 A 在执行方法 A 时想要获取 Object-B 的锁,而线程 B 已经获取了 Object-B 的锁。

另一方面,线程 B 也试图获取 Object-A 的锁,而线程 A 已经获取了 Object-A 的锁。在这种情况下,两个线程都将无法完成其执行,并将等待释放锁。这种情况被称为死锁

How to Avoid Deadlock in Java

如何在 Java 中检测死锁?

检测死锁的以下方法:

  • 首先,我们查看并理解代码,如果我们发现嵌套的同步块或尝试获取对不同对象的锁或从另一个同步方法调用同步方法,这些原因都会导致死锁情况。
  • 检测死锁的另一种方法是使用 io 门户。它允许我们上传线程转储并对其进行分析。
  • 我们还可以使用 jConsoleVisualVM 来检测死锁。它们显示了哪些线程正在被锁定以及在哪个对象上。

考虑两个银行账户同时试图相互转账的情况。这意味着一个账户被借记,另一个账户被贷记,反之亦然。让我们在 Java 程序中实现这种情况。

在下面的示例中,我们可以轻松检测到死锁。因为两个线程正在同时相互发送金钱,这会导致死锁情况。

Account.java

在上面的示例中,有两个线程试图同时将钱转给对方。这会造成死锁情况,因为线程尝试以相反的顺序获取锁。

如何在 Java 中避免死锁?

虽然不可能完全避免死锁情况,但我们可以通过以下方式避免它:

  • 避免不必要的锁:我们应该只对必需的成员使用锁。不必要的锁使用会导致死锁情况。我们建议您使用无锁数据结构。如果可能,请使您的代码摆脱锁。例如,不要使用同步的 ArrayList,而是使用 ConcurrentLinkedQueue
  • 避免嵌套锁:避免死锁的另一种方法是,如果我们已经为其中一个线程提供了锁,则避免为多个线程提供锁。因为我们必须避免为多个线程分配锁。
  • 使用 Thread.join() 方法:如果两个线程在等待对方无限期完成,您可能会遇到死锁。如果您的线程必须等待另一个线程完成,最好将 join 与您想等待线程完成的最长时间一起使用。
  • 使用锁顺序:始终为每个锁分配一个数字值。在获取具有较高数字值的锁之前,先获取具有较低数字值的锁。
  • 锁超时:我们还可以为线程获取锁指定时间。如果线程未获取锁,则线程必须等待特定时间,然后重试获取锁。

让我们通过一个例子来理解。

AvoidDeadlockExamplet.java

输出

How to Avoid Deadlock in Java

我们建议您,如果您处理多个锁,请始终在获取具有较高数字值的锁之前,先获取具有较低数字值的锁。