线程-死锁

  

  有多个线程执行的程序中,某个线程t1需要锁住两个对象obj1和obj2才能完成任务,而线程t2也需要锁住这两个对象,t1首先锁住obj1,t2首先锁住obj2,t1需要等待t2将obj2的锁放开,而t2也需要等待t1将obj1放开才能执行完,此时便进入了死锁状态。

死锁演示程序如下:

 1 public class TestDeadLock implements Runnable
 2 {
 3     public int flag = 1;
 4     static Object o1 = new Object(), o2 = new Object();
 5     public void run()
 6     {
 7         System.out.println("flag=" + flag);
 8         if(flag == 1)
 9         {
10             synchronized(o1)
11             {
12                 try {
13                         Thread.sleep(500);
14                     }
15                 catch (Exception e)
16                {
17                     e.printStackTrace();
18                 }
19                 synchronized(o2)
20                 {
21                     System.out.println("1");
22                 }
23             }
24         }
25         if(flag == 0) {
26             synchronized(o2)
27             {
28                 try
29                 {
30                     Thread.sleep(500);
31                 } catch (Exception e)
32                 {
33                     e.printStackTrace();
34                 }
35                 synchronized(o1)
36                 {
37                     System.out.println("0");
38                 }
39             }
40         }
41     }
42
43     public static void main(String[] args)
44     {
45         TestDeadLock td1 = new TestDeadLock();
46         TestDeadLock td2 = new TestDeadLock();
47         td1.flag = 1;
48         td2.flag = 0;
49         Thread t1 = new Thread(td1);
50         Thread t2 = new Thread(td2);
51         t1.start();
52         t2.start();
53
54     }
55 }

程序执行结果:

  flag=1
      flag=0

很显然,程序只是执行了run方法的第一行,而后的打印都没有执行,两个线程都在等待,便进入了死锁状态。

  那么该如何避免死锁呢?

什么是死锁,如何避免死锁?
     线程A需要资源X,而线程B需要资源Y,而双方都掌握有对方所要的资源,这种情况称为死锁(deadlock),或死亡拥抱(the deadly embrace)。

在并发程序设计中,死锁 (deadlock) 是一种十分常见的逻辑错误。通过采用正确的编程方式,死锁的发生不难避免。

  死锁的四个必要条件

  在计算机专业的本科教材中,通常都会介绍死锁的四个必要条件。这四个条件缺一不可,或者说只要破坏了其中任何一个条件,死锁就不可能发生。我们来复习一下,这四个条件是:

  1. 互斥(Mutual exclusion):存在这样一种资源,它在某个时刻只能被分配给一个执行绪(也称为线程)使用;
  2. 持有(Hold and wait):当请求的资源已被占用从而导致执行绪阻塞时,资源占用者不但无需释放该资源,而且还可以继续请求更多资源;
  3. 不可剥夺(No preemption):执行绪获得到的互斥资源不可被强行剥夺,换句话说,只有资源占用者自己才能释放资源;
  4. 环形等待(Circular wait):若干执行绪以不同的次序获取互斥资源,从而形成环形等待的局面,想象在由多个执行绪组成的环形链中,每个执行绪都在等待下一个执行绪释放它持有的资源。

解除死锁的必要条件

  不难看出,在死锁的四个必要条件中,第二、三和四项条件比较容易消除。通过引入事务机制,往往可以消除第二、三两项条件,方法是将所有上锁操作均作为事务对待,一旦开始上锁,即确保全部操作均可回退,同时通过锁管理器检测死锁,并剥夺资源(回退事务)。这种做法有时会造成较大开销,而且也需要对上锁模式进行较多改动。

  消除第四项条件是比较容易且代价较低的办法。具体来说这种方法约定:上锁的顺序必须一致。具体来说,我们人为地给锁指定一种类似“水位”的方向性属性。无论已持有任何锁,该执行绪所有的上锁操作,必须按照一致的先后顺序从低到高(或从高到低)进行,且在一个系统中,只允许使用一种先后次序。

  请注意,放锁的顺序并不会导致死锁。也就是说,尽管按照 锁A, 锁B, 放A, 放B 这样的顺序来进行锁操作看上去有些怪异,但是只要大家都按先A后B的顺序上锁,便不会导致死锁。

解决方法:

  1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务; (细化处理逻辑,执行一段逻辑后便回滚或者提交,然后再执行其它逻辑,直到事物执行完毕提交)
  2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
  3 优化程序,检查并避免死锁现象出现;
  4 .对所有的脚本和SP都要仔细测试,在正是版本之前。
  5 所有的SP都要有错误处理(通过@error)
  6 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁

另外参考的解决方法:

  按同一顺序访问对象
      如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。
   
      避免事务中的用户交互
    避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。
   
    保持事务简短并在一个批处理中
    在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。
   
    使用低隔离级别
    确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。
   
    使用绑定连接
    使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。

本文下半段转自:http://www.cnblogs.com/cxd4321/archive/2012/05/28/2521542.html

线程-死锁

时间: 2025-01-16 04:06:16

线程-死锁的相关文章

线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.Exit处理数据同步※ 使用Mutex处理进程间数据同步※ 使用Semaphore处理数据同步※ 线程死锁 □ 使用lock处理数据同步 假设有一个类,主要用来计算该类2个字段的商,在计算商的方法之内让被除数自减,即被除数有可能为零.使用lock语句块保证每次只有一个线程进入该方法. class Th

线程死锁

所谓的线程死锁,是指在多线程运行的过程中,线程1拥有锁a,而需要锁b来继续执行, 而此时,线程2拥有锁b而需要锁a来继续执行,那么此时会形成死锁,两个线程会同时等待. 在编程的过程中应尽量的避免线程死锁. 有时在面试中会要求写出一个死锁的程序演示,如下: 1 //写一个死锁程序 2 public class DeadLock { 3 //主程序执行 4 public static void main(String[] args) { 5 Thread thread1 = new Thread(n

在Linux下线程死锁的四个条件

一.死锁的原因和必要条件 1.死锁的概念 一般情况下,如果同一个线程先后两次调用lock,在第一次调用时,由于锁已经被占,该线程会挂起等待别的线程释放锁,然而锁正是被自己占着的,该线程又被挂起,没有机会释放锁,因此,就永远处于挂起等待状态了,这叫做死锁(Deadlock).另种典型的死锁情形是这样:线程A获 得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永

Java笔记六.线程同步、线程死锁

线程同步.线程死锁 在上一篇文章中,有一个模拟售卖火车票系统,在卖车票的程序代码中,极有可能碰到一种意外,就是同一张票号被打印两次多次,也可能出现打印出0甚至负数的票号.具体表现为:假设tickets的值为1的时候,线程1刚执行完if(tickets>0)这行代码,正准备执行下面的代码,就在这时,操作系统将CPU切换到了线程2上执行,此时tickets的值仍为1,线程2执行完上面两行代码,tickets的值变为0后,CPU又切回到了线程1上执行,线程1不会再执行if(tickets>0)这行代

JAVA笔记14__多线程共享数据(同步)/ 线程死锁 /

/** * 多线程共享数据 * 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行. * 多线程共享数据的安全问题,使用同步解决. * 线程同步两种方法: * 1.同步代码块 * synchronized(要同步的对象){ 要同步的操作 } * 2.同步方法 * public synchronized void method(){ 要同步的操作 } */ public class Main { public static void main(

atitit.线程死锁 卡住无反应 的原因in cmd调用的解决方案  v3 q39

atitit.线程死锁 卡住无反应 的原因in cmd调用的解决方案  v3 q39 1. 问题::线程死锁  卡住无反应1 1.1. 分类:: cmd调用,  net io  , file  io 调用, multi thread调用same var的时候儿..1 1.2. 原因readLine()是阻塞方法1 1.3. 调用same var1 2. 解决之道::2 2.1. 使用了cmd /k走死锁兰...改成个/c佐ok兰..2 2.2. Watchdog2 3. Ref3 1. 问题::

进程/线程死锁产生的原因以及如何避免死锁

线程死锁产生的必要条件: (1)互斥条件:一个资源每次只能被一个进程使用. (2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放. (3)不可剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺. (4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系. 如何避免死锁,这点我觉得Erlang模型值得参考.在编程过程中,有一些避免死锁的经验: (1) 等待某个资源时,使用超时机制(例如Erlang中的receive可以加一个超时): (2) 采用消息通信的通信机

GCD中的线程死锁问题

GCD 确实好用 ,很强大,相比NSOpretion 无法提供 取消任务的功能. 如此强大的工具用不好可能会出现线程死锁. 如下代码: - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"=================4"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"=================5"); }); NSLog(@"===

Atitit.线程 死锁 跑飞 的检测与自动解除 与手动解除死锁 java c# .net php javascript.

Atitit.线程 死锁 跑飞 的检测与自动解除 与手动解除死锁 java c# .net php javascript. 1. 现象::主程序卡住无反应,多行任务不往下执行 1 2. 原因::使用jv jprofile查看线程,原来俩个线程死锁了.. 1 3. Java的缺点,默认不能自动解除死锁 1 4. 自动检测与解除死锁::使用看门狗watchdog 2 4.1. 死锁检测算法(太麻烦,不推荐) 2 4.2. 硬件看门狗 2 4.3. 软件看门狗的实现--TIMER 2 4.4. LIN

GCD 之线程死锁

GCD 确实好用 ,很强大,相比NSOpretion 无法提供 取消任务的功能. 如此强大的工具用不好可能会出现线程死锁. 如下代码: - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"=================4"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"=================5"); }); NSLog(@"===