多线程中的死锁示例

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程中的死锁示例相关的知识,希望对你有一定的参考价值。

通过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在它之前离开了同一个块呢?

答案

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

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

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

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

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

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

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

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

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

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

另一答案

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

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

另一答案

考虑一下,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

另一答案

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

以上是关于多线程中的死锁示例的主要内容,如果未能解决你的问题,请参考以下文章

网络编程基础--多线程---concurrent.futures 模块---事件Event---信号量Semaphore---定时器Timer---死锁现象 递归锁----线程队列queue(示例代码

java死锁示例及其发现方法

EF 多线程TransactionScope事务异常"事务EFTransaction类定义:与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。"(示例代码

线程同步

死锁使用 std::mutex 保护多线程中的 cout

使用多线程代码在一张表上发生 MySQL 死锁