多线程中的死锁示例

时间:2018-03-05 11:00:20

标签: java multithreading synchronization sleep thread-sleep

在浏览死锁的示例时,我遇到了这段代码:

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();  
}  
}  

输出:

Thread 1: locked resource 1

Thread 2: locked resource 2

Timeout due to heavy load

据我所知这是流程:

  1. Thread1进入resource1上的同步块并休眠
  2. Thread2进入resource2上的同步块并休眠
  3. 我怀疑的是,如果Thread1在Thread2之前恢复执行,因为它在Thread2之前休眠,那么为什么它不会进入resource2上的同步块,因为那时Thread2必须在{{1}上离开同步块并完全避免死锁?在Thread1离开之前的同一个块之后,为什么Thread2进入resource2上的同步块有什么疑问呢?

4 个答案:

答案 0 :(得分:2)

  

我的疑问是,如果Thread1在Thread2之前恢复执行,那么   在Thread2之前睡觉

记住第一条多线程规则,如果你开始使用2个线程,则无法保证首先启动哪个线程,因此无法确定Thread1是先启动还是先睡眠,或者Thread2是否先启动并先睡眠

  

那么为什么它从那时起就没有进入resource2s上的同步块   Thread2必须离开resource2上的同步块并避免使用   完全死锁?

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

  • 获取第一个对象的锁定
  • 睡眠
  • 获取第二个对象的锁定
  • 释放第二个对象的锁定
  • 释放第一个对象的锁

因此,两个线程首先获得锁定然后再进入休眠状态,而Thread1在唤醒时Thread2没有获得resource2的锁定的可能性非常小,所以几乎每次你得到一个僵局。

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

"可能" 在以下情况下没有陷入僵局:

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

我认为你需要注意的是,两个线程都会被唤醒时获取两个对象的锁定。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

class Model1Index(indexes.Index):
    name = fields.Text(model_attr='name')
    caption = fields.Text(model_attr='model2__caption')
    class Meta:
        model = Model1
        settings.INDEXES['source_params'] = {'sql_field_string': ['name'],}

现在进入睡眠状态并且线程2在进入睡眠状态之前按照说明完成。

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

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

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

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

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

但它无法完成它的执行,因为Thread2已经锁定 synchronized (resource2) { System.out.println("Thread 1: locked resource 2"); } 。由于它在resource2上同步,因此除非Thread2在resource2上释放锁定,否则它将不会进一步执行。这是保持和等待的经典示例。

同样,当Thread2唤醒时,由于等待resourse2

答案 3 :(得分:0)

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