前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程之死锁

并发编程之死锁

作者头像
爱撒谎的男孩
发布2019-12-31 15:12:48
4000
发布2019-12-31 15:12:48
举报
文章被收录于专栏:码猿技术专栏码猿技术专栏

文章目录

1. 并发编程之死锁

1.1. 定义

1.2. 产生的条件

1.3. 分析

1.4. 避免死锁

1.4.1. 总结

1.5. 参看文章

并发编程之死锁

定义

  • 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

产生的条件

  1. 互斥条件:所谓互斥就是线程在某一时间内独占资源。
    1. 每一次的资源只能被一个线程使用
  2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
    1. 一个线程在获取资源的时候,此时另外一个线程请求资源的时候保持不放
  3. 不剥夺条件:线程已获得资源,在末使用完之前,不能强行剥夺。
    1. 资源只能被持有者线程主动释放,不能被强行剥夺
  4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。

分析

  • 我们可以把锁看做一个资源,这个资源正好符合互斥条件不剥夺条件的要求。那么可能产生死锁的代码特征就是在持有一个锁的情况下去申请另外一个锁,这个通常就是意味着嵌套
  • 一个线程在已经持有一个锁的情况下再次申请这个锁(比如,一个类的同步方法调用该类的另外一个同步方法)并不会导致死锁,这是因为Java中的锁(包括内部锁synchronized和显示锁Lock)都是可重入的(Reentrant),这种情况下线程再次申请这个锁是可以成功的。因此我们必须使用不同对象的锁
代码语言:javascript
复制
package com.demo.test;

/**
 * 一个简单的死锁类
 * t1先运行,这个时候flag==true,先锁定obj1,然后睡眠1秒钟
 * 而t1在睡眠的时候,另一个线程t2启动,flag==false,先锁定obj2,然后也睡眠1秒钟
 * t1睡眠结束后需要锁定obj2才能继续执行,而此时obj2已被t2锁定
 * t2睡眠结束后需要锁定obj1才能继续执行,而此时obj1已被t1锁定
 * t1、t2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。 
 */
public class DeadLock implements Runnable{
    
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();
    private boolean flag;
    
    public DeadLock(boolean flag){
        this.flag = flag;
    }
    
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + "运行");
        
        if(flag){
            synchronized(obj1){
                System.out.println(Thread.currentThread().getName() + "已经锁住obj1");
                try {  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                synchronized(obj2){
                    // 执行不到这里
                    System.out.println("1秒钟后,"+Thread.currentThread().getName()
                                + "锁住obj2");
                }
            }
        }else{
            synchronized(obj2){
                System.out.println(Thread.currentThread().getName() + "已经锁住obj2");
                try {  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                synchronized(obj1){
                    // 执行不到这里
                    System.out.println("1秒钟后,"+Thread.currentThread().getName()
                                + "锁住obj1");
                }
            }
        }
    }

}

避免死锁

  1. 锁排序法,相关线程使用全局统一的顺序申请锁
  2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    1. 比如使用tryLock(timeout) : 尝试获取锁,如果获取不到不会持续等待
  3. 死锁检测

总结

1、让程序每次至多只能获得一个锁。当然,在多线程环境下,这种情况通常并不现实。

2、设计时考虑清楚锁的顺序,尽量减少嵌在的加锁交互数量。

3、既然死锁的产生是两个线程无限等待对方持有的锁,那么只要等待时间有个上限不就好了。当然synchronized不具备这个功能,但是我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后便会返回一个失败信息。

参看文章

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 并发编程之死锁
    • 定义
      • 产生的条件
        • 分析
          • 避免死锁
            • 总结
          • 参看文章
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档