ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

避免死锁危险

2021-04-27 20:02:13  阅读:180  来源: 互联网

标签:程序 避免 获取 死锁 线程 危险 等待 资源


在并发环境中,我们为了保证共享可变数据的线程安全性,需要使用加锁机制,如果锁使用不当可能会引起死锁,线程饥饿等问题。

在Java应用程序中如果发生死锁,程序是无法自动恢复的,严重会造成程序崩溃,所以开发中在设计阶段就要规避死锁发生的情况。

什么是死锁

死锁:每个线程拥有其他线程需要的资源,同时又等待其他线程拥有的资源,并且每个线程在获得所需要的资源前都不会放弃已经拥有的资源。

程序死锁发生的场景:

1)交叉锁导致死锁

在线程A持有锁L并想获取锁R的同时,线程B持有锁R并尝试获得锁L,那么这两个线程将永远的阻塞下去。交叉锁的发生一般是因为线程以不同的顺序获取锁。

2)资源死锁

内存不足或者我们在程序中使用了线程池和信号量对资源进行限制时,两个线程互相等待彼此释放资源而进入永久阻塞。

3)死循环死锁

程序由于代码缺陷或者重试机制而使代码陷入死循环,造成了内存和cpu的大量消耗而使线程进入阻塞。

当Java程序发生死锁时,阻塞的线程将永远不能使用了,而且可能造成程序停止或者使CPU飙高使程序性能很差。恢复程序的唯一方式就是重启应用。

死锁的发生大多数是偶然情况,并不代表一个类发生死锁,它就一直死锁,这也是死锁难以排查的原因。

通过死锁发生的场景我们可以总结出死锁发生的条件:

  • 互斥:即锁具有排他性,只有一个线程能够获取锁;

  • 占有且等待:线程获取到锁时,如果需要的资源没有获取到将一直阻塞等待需要的资源;

  • 不可抢占:获取锁的线程持有的资源不能被其他线程抢占;

  • 循环等待:陷入死锁等待的线程一定是形成了一个循环等待环路。

死锁的检测

如果一个程序一次最多获得一个锁,那么就不会发生死锁问题,但是开发中经常出现程序需要获取多个锁的场景,那么这个时候就必须考虑锁的顺序问题。

如果所有的线程以固定的顺序获取锁也是不会出现死锁问题的,当线程试图以不同的顺序来获取锁时,死锁将会发生。

下面的示例将会发生死锁:

public class DeadlockTest {
    //创建两个锁对象
    private final Object leftMonitor = new Object();
    private final Object rightMonitor = new Object();
    /**
     * 持有L锁想要获取R锁
     */
    @SneakyThrows
    public void leftForRight() {
        synchronized (leftMonitor){
            //休眠一下,给R加锁的机会
            TimeUnit.SECONDS.sleep(1);
            synchronized (rightMonitor){
                System.out.println("leftForRight获取到锁");
            }
        }
    }

    /**
     * 持有R锁获取L锁
     */
    public void rightForLeft() {
        synchronized (rightMonitor){
            synchronized (leftMonitor){
                System.out.println("rightForLeft获取到锁");
            }
        }
    }

    public static void main(String[] args) {
        DeadlockTest deadlockTest = new DeadlockTest();
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(()->{
            deadlockTest.leftForRight();
        });
        executor.execute(()->{
            deadlockTest.rightForLeft();
        });
        executor.shutdown();

    }

}

我们可以通过JDK提供的jstack或者jconsole工具查看死锁信息。

jstack -l pid查看堆栈信息:

图片

或者jconsole连接到进程上:

图片

图片

通过堆栈信息能够很直接看到死锁信息。

linux环境下dump出堆栈信息的方法我们后续再聊。

死锁的避免 

我们可以通过打破死锁发生的条件来避免死锁。

程序中的业务要求我们必须使用独占锁而不能使用共享锁,那我们就不能打破锁的互斥性。

破坏占有且等待:一次性申请所有资源;

破坏不可抢占:使用显示锁Lock中的tryLock功能来代替内置锁synchronized,可以检测死锁和从死锁中恢复过来。使用内置锁的线程获取不到锁会被阻塞,而显式锁可以指定一个超时时限(Timeout),在等待设置的时间后tryLock就会返回一个失败信息,也会释放其拥有的资源。

破坏循环等待:使线程按照固定的顺序获取锁,在设计中我们应尽量减少锁的交互数量,提前设计好锁的顺序并严格遵守。

结束语 

并发编程系列基础知识的学习到此结束了,后续如果遇到相关的知识再补充。

下一个系列《Java基础》扬帆启航,类加载、数据结构(包括线程安全的数据结构)等知识将与你相遇。

标签:程序,避免,获取,死锁,线程,危险,等待,资源
来源: https://blog.csdn.net/qq_32428485/article/details/115520895

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有