如何在 Java 中实现死锁示例

How to Implements Dead Lock Example in Java

我正在阅读有关 Java Deadlock 情况的信息,我了解可能发生死锁的位置,但我遇到了如何实现以下示例的问题。

public class DeadLockDemo {

    public void method1() {
        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");

            synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");
            }
        }
    }

    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
}

我试过了,但没有出现死锁情况。

public class DeadLockDemo {
    public void method1() {
        synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");
            }
        }
    }
    public void method2() {
        synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");
            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
    public static void main(String[] args) {  
        DeadLockDemo obj=new DeadLockDemo();
        Thread t1 = new Thread() {  
          public void run() { obj.method1(); }  
        };  
        Thread t2 = new Thread() {  
          public void run() {  obj.method2(); }  
        };  
        t1.start();  
        t2.start();  
      }  
}

如何更正上面的示例以获得死锁情况?

您不能保证会遇到死锁,因为您不知道何时安排线程:线程 1 可能在线程 2 启动之前就已完成。 (您可以通过在其中一个线程的开头添加一个短暂的睡眠来模拟这一点。)

您可以通过使用 CountDownLatch 来强制死锁,以确保线程等待另一个线程获取第一个资源:

final CountDownLatch latch = new CountDownLatch(2);

Thread t1 = new Thread() {
  public void run() {
    synchronized (resource1) {
      latch.countDown();
      latch.await(); // InterruptedException omitted.

      synchronized (resource2) {}
    }
  }
};

Thread t2 = new Thread() {
  public void run() {
    synchronized (resource2) {
      latch.countDown();
      latch.await(); // InterruptedException omitted.

      synchronized (resource1) {}
    }
  }
};

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

您的示例的问题在于,其中一个线程将在另一个线程有机会 运行1 之前获取两个锁。然后它将释放它们并退出,第二个线程将能够获取锁;即没有死锁。

如果您更改了这两种方法来执行此操作:

  1. 抢一把锁
  2. 睡一会儿(例如一秒钟左右)
  3. 抓住另一把锁

如果他们以相反的顺序抢锁,那么你应该观察到一个死锁。 (甚至可能甚至调用 yield 而不是 sleep 也会这样做......尽管这种行为比你睡觉时更难以预测。)

只有当各个线程获取锁的时间重叠时才会发生死锁。这就是为什么死锁倾向于通过测试。


1 - Java 规范实际上并不需要这个。然而,线程调度程序将以允许死锁的方式取消调度/重新调度线程的机会......没有一些sleep形式的"help"......是零。

您的代码包含可能 死锁 - 您只会在特定情况下遇到它。如果您不想按照其他答案中的建议更改代码,您也可以使用调试器进入死锁。

  1. method1method2
  2. 上设置断点
  3. 运行调试。您的两个线程应该在这些断点处暂停等待(您看到线程的位置取决于您的 IDE/Debugger,在 Eclipse 中,您会在左上角看到线程列表,并且可以通过单击它们在线程之间切换)
  4. 将线程 1 推进一步以进入 synchronized (String.class)
  5. 将线程 2 推进一步以进入 synchronized (Integer.class)
  6. 至此僵局在所难免。如果继续这两个线程,您应该会看到预期的结果。