多线程中的死锁示例

问题描述 投票:2回答:4

通过Deadlock上的示例,我遇到了这段代码:

public class TestDeadlockExample1 {  
public static void main(String[] args) {  
final String resource1 = "ratan jaiswal";  
final String resource2 = "vimal jaiswal";  
// t1 tries to lock resource1 then resource2  
Thread t1 = new Thread() {  
  public void run() {  
      synchronized (resource1) {  
       System.out.println("Thread 1: locked resource 1");  

       try { Thread.sleep(100);} catch (Exception e) {}  

       synchronized (resource2) {  
        System.out.println("Thread 1: locked resource 2");  
       }  
     }  
  }  
};  

// t2 tries to lock resource2 then resource1  
Thread t2 = new Thread() {  
  public void run() {  
    synchronized (resource2) {  
      System.out.println("Thread 2: locked resource 2");  

      try { Thread.sleep(100);} catch (Exception e) {}  

      synchronized (resource1) {  
        System.out.println("Thread 2: locked resource 1");  
      }  
    }  
  }  
};  


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

OUTPUT:

Thread 1: locked resource 1

Thread 2: locked resource 2

Timeout due to heavy load

据我说这是流程:

  1. Thread1进入resource1上的同步块并休眠
  2. Thread2进入resource2上的同步块并休眠

我怀疑的是,如果Thread1在Thread2之前恢复执行,因为它在Thread2之前休眠,那么为什么它不会进入resource2since上的同步块,那时Thread2必须离开resource2上的同步块并完全避免死锁?同样是为什么Thread2在resource1a上进入同步块之后,为什么Thread1在它之前离开了同一个块呢?

java multithreading synchronization sleep thread-sleep
4个回答
2
投票

我怀疑的是,如果Thread1在Thread2之前恢复执行,因为它在Thread2之前就已经睡了

记住多线程的第一条规则,如果你开始2个线程,就不能保证首先启动哪个线程,所以你不能确定你的Thread1是先启动睡眠还是先启动并先睡眠。

那么为什么它不进入resource2s上的同步块呢?那时Thread2必须离开resource2上的同步块并完全避免了死锁?

请注意,在两个线程中,您都是这样做的:

  • 得到第一个对象的锁
  • 睡觉
  • 得到第二个对象的锁
  • 释放第二个对象的锁
  • 释放第一个对象的锁

因此,两个线程将首先获得锁定然后进入休眠状态,并且在Thread1唤醒时Thread2没有获得resource2锁定的可能性非常小,因此几乎每次遇到死锁时都是如此。

我认为这个例子是为了证明死锁。

如果出现以下情况,你“可能”没有陷入僵局:

  • 在Thread2中,你在try { Thread.sleep(100);} catch (Exception e) {}之前有synchronized (resource2) {。因为有了这个,Thread1可能会在Thread2唤醒时获得resource2的锁定。

我想你要注意的是,两个对象的锁定都将在线程唤醒时获取。


1
投票

问题是你设计的线程并不是你所在的系统,没有什么能保证它们在你调用start()之后立即启动,也不会保证它们会使睡眠状态保持同步。这就是死锁进程的重点,因为系统在进程(进程的线程)之间不断切换,可能存在一个进程需要另一个进程正在使用的资源的情况。除非你设计,否则你永远不能假设进程中有时间限制更复杂的同步结构:/

正如其中一条评论所解释的那样,一般情况下发生死锁的原因之一是一个进程在完成使用后不会释放资源。嵌套同步块就是这种情况。


1
投票

考虑一下,Thread1已完成这些说明

synchronized (resource1) {  
   System.out.println("Thread 1: locked resource 1");  

   try { Thread.sleep(100);} catch (Exception e) {}  

现在进入睡眠状态并且在睡眠前按照说明完成thread2。

synchronized (resource2) {  
  System.out.println("Thread 2: locked resource 2");  

  try { Thread.sleep(100);} catch (Exception e) {}  

现在,当Thread1唤醒时,它将尝试执行

 synchronized (resource2) {  
    System.out.println("Thread 1: locked resource 2");  
   } 

但它无法完成它的执行,因为Thread2已经锁定了resource2。由于它在resource2上同步,因此除非Thread2释放对resourse2的锁定,否则它不会进一步执行。这是Hold和Wait的典型例子。

类似地,当Thread2唤醒时,它无法进一步执行,因为它正在等待由Resource1锁定的thread1


0
投票

正如问题的评论部分所指出的那样,我错过了死锁的一个重要线索,即同步块互相嵌套,因此在整个父同步块完成之前,父资源上的锁定不会被释放。为了验证,我删除了嵌套的大括号并使块独立,似乎避免了死锁。如果我的理解是正确的,请告诉我

© www.soinside.com 2019 - 2024. All rights reserved.