• 并发编程之死锁


     
    产生死锁的4个必要条件
    • 互斥条件:在一段时间内某资源仅为一个线程所占有
    • 不可剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走
    • 请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有
    • 循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。

    产生死锁的情况

    • 多个锁的交叉(交叉锁)
                    交叉锁引起的线程会进入BLOCKED状态,CPU资源栈用不高,很容易借助工具发现
                    情景描述:线程A持有锁1,等待获取锁2;线程B持有锁2,等待获取锁1。
    private final static Object MUTEX_READ = new Object();
    private final static Object MUTEX_WRITE = new Object();
    public void read(){
        synchronized (MUTEX_READ) {
            synchronized (MUTEX_WRITE) {
            }
        }
    }
    public void write(){
        synchronized (MUTEX_WRITE) {
            synchronized (MUTEX_READ) {
            }
        }
    }
    • 内存不足
    • 一问一答式的数据交换
                服务器开启了某个端口,等待客户端的访问。客户端发送请求后等待服务器的响应,服务器因为某种原因错过了客户端的请求,让在等待请求。此时,服务端可客户端都在等待对方发送数据。
    • 数据库锁
                比如某个线程执行了for update语句后退出了事务,其他线程访问的时候就会陷入死锁
    • 文件锁
                获取文件锁的线程意外退出,其它线程无法获取该文件锁会进入死锁。
    • 死循环引起的死锁【系统假死,查看堆栈信息无法发现任何死锁迹象,CPU占有率居高不下,是最为致命以及最难排查的死锁现象】

    处理死锁的方法

     通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个条件,来防止死锁的发生。【在死锁产生的四个必要条件中,“互斥条件”是无法破坏的,破坏“互斥条件”会造成结果的不可再现性】

    • 破坏“不可剥夺条件”
            允许对资源实行抢夺。
            方法一:如果占有某些资源的一个线程进行进一步资源请求被拒绝,则该线程必须释放它最初占有的资源【如果有需要,可再次请求这些资源和另外的资源】。
            方法二:允许优先级高的线程抢占优先级低的线程的资源。
    • 破坏“请求和保持条件”
           在系统中不允许进程在已获得某种资源的情况下,申请其他资源。
              方法一:采用“ 一次性分配”方案,即:创建进程时,要求它申请所需的全部资源,系统或满足其所有要求,或什么也不给它。
              方法二:要求每个进程提出新的资源申请前,释放它所占有的资源。
    • 破坏“循环等待条件”
              将系统中的所有资源进行编号,线程必须按照顺序申请资源。
     
    避免死锁的处理方式
    • 加锁顺序:给所有的锁排序,线程只能按照升序(降序)的获取锁。【破坏循环等待条件】
    • 加锁超时:给线程设置获取锁的最大等待时间,如果超时则放弃对该锁的请求并释放已占有的锁,等待一段时间后再次请求。【破坏请求和保持条件】
    • 死锁检测
                主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。每当一个线程请求或者获得了锁,会在线程和锁相关的数据结构中将其记下。
                线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。
     
    检测和解除死锁
     
        由于操作系统有并发、共享以及随机性等特点,通过预防和避免的手段达到排除死锁的目的是很困难的。一种简便的方法是系统为进程分配资源时,不采取任何限制性措施,但是提供了检测和解脱死锁的手段:能发现死锁并从死锁状态中恢复出来。因此,在实际的操作系统中往往采用死锁的检测和解除方法来排除死锁。
            死锁检测和解除是指系统设有专门的机制,当死锁发生时,该机制能够检测到死锁发生的位置和原因,并能通过外力破坏死锁发生的必要条件,从而使得并发进程从死锁状态中恢复出来。
    • 解除死锁
                1) 资源剥夺法:挂起某些死锁进程,并释放它的资源,将这些资源分配给其他的死锁进程。【但应防止被挂起的进程长时间得不到资源,而处于资源匮乏的状态】
                2) 撤销线程法:强制撤销部分、甚至全部死锁线程。【撤销的原则可以按进程优先级和撤销进程代价的高低进行】
                3) 进程回退法:让一(多)个线程回退到足以回避死锁的地步【要求系统保持进程的历史信息,设置还原点】
     
     
  • 相关阅读:
    04.日志管理
    刷爆美国朋友圈的超燃短片:年轻人为什么要奋斗?
    【逗比作孽呀】网站缓存优化
    来看看这20个顶尖的开源项目!
    nginx处理问题笔记
    -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
    一个创业公司倒下的128小时
    快速打造跨平台开发环境 vagrant + virtualbox + box
    【Git 使用笔记】第四部分:git在公司中的开发流程
    新购买的vps应该做的几件事情
  • 原文地址:https://www.cnblogs.com/BlueStarWei/p/11636566.html
Copyright © 2020-2023  润新知