专栏首页爱撒谎的男孩并发编程之死锁

并发编程之死锁

文章目录

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),这种情况下线程再次申请这个锁是可以成功的。因此我们必须使用不同对象的锁
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方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后便会返回一个失败信息。

参看文章

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 并发编程之线程管理

    爱撒谎的男孩
  • Thread初探

    爱撒谎的男孩
  • Lock

    从上面的synchronized释放锁可以看出,只有synchronized代码块执行完毕或者异常才会释放,如果代码块中的程序因为IO原因阻塞了,那么线程...

    爱撒谎的男孩
  • Java的面试基础题(三)

    List以特定索引来存取元素,可以有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value p...

    Demo_Yang
  • Android面试常见题

    基本数据类型的==比较的值相等. 类的==比较的内存的地址,即是否是同一个对象,在不覆盖equals的情况下,同比较内存地址,原实现也为 == ,如Strin...

    Demo_Yang
  • 2019年Java面试题基础系列228道(3),查漏补缺!

    https://cloud.tencent.com/developer/article/1549815

    程序员追风
  • 高并发编程-ReentrantLock公平锁深入解析

    ReentrantLock是一个可重入的互斥锁,它不但具有synchronized实现的同步方法和同步代码块的基本行为和语义,而且具备很强的扩展性。Reentr...

    JavaQ
  • 精选30道Java多线程面试题

    1、线程和进程的区别 2、实现线程有哪几种方式? 3、线程有哪几种状态?它们之间如何流转的? 4、线程中的start()和run()方法有什么区别? 5、怎么终...

    Java技术栈
  • java基础

    当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方...

    大学里的混子
  • Java Concurrent synchronized 使用&原理

    sychronized 是Java语法层面的同步策略,可以用来修饰instance变量、object reference(对象引用)、static函数和clas...

    邹志全

扫码关注云+社区

领取腾讯云代金券