出现死锁的场景分析及解决方法

news2024/9/27 19:22:47

在上一篇互斥锁的时候最后使用Account.class作为互斥锁,来解决转载问题,所有的账户转账操作都是串行的,性能太差。

我们可以考虑缩小锁定的范围,使用细粒度的锁,来提高并行度。例如用两把锁,转出账本一把,转入账本一把,在transfer()方法内部,我们首先尝试锁定转出账户this,然后尝试锁定转入账户target,只有当两者都成功时,才执行转账操作。

这样改造使用细粒度的锁,可以提高并行度,但是是有代价的,这个代价就是可能会死锁。

导致死锁的特殊场景就是,例如同一时间,账户A给账户B转账,账户B也在给账户A转账,都第一时间获取到了锁定转出账本的锁,在获取转入账本的锁时发现锁以及被抢占,就出现死锁。

死锁的定义:一组互相竞争资源的线程因相互等等,导致“永久”阻塞的现象。

用图来解释就是:
image

如何预防死锁

并发程序一旦死锁,一般没有没有特别好的方法,很多时候只能重启应用。解决死锁问题最好的方法是规避死锁。

出现死锁的四个条件:

  • 互斥,共享资源X和Y只能被一个线程占用;
  • 占有且等待,线程T1已经获得共享资源X,在等待共享资源Y的时候,不释放共享资源X;
  • 不可抢占,其他现场不能强行抢占T1占有的资源
  • 循环等待,线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源,就是循环等待。

只要我们破坏其中一个,就可以成功避免死锁。

互斥这个条件是不能破坏的,加锁就是为了互斥,其他三个条件是有办法破坏的:

  • 对于“占用且等待”这个条件,我们可以一次性申请所有资源,这样就不存在等待了
  • 对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放占有的资源,这样不可抢占的这个条件就破坏掉了。
  • 对于“循环等待”这个条件,可以靠按顺序申请资源来预防,所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了。

####### 1.破坏占用且等待条件

理论上破坏这个条件,可以一次性申请所有资源。

对应到银行转账可以增加一个账本管理员,然后只允许账本管理员从文件架上拿账本,所有的转账都要先从账本管理员那拿到账本。例如账户A给账户B转账,账本管理员如果发现只有账本A在,就不会给柜员,来操作转账,只有账本A和账本B都在,才会给柜员进行转账操作,这样就保证了“一次性申请所有资源”。

对应的代码实现就是,“同时申请”这个操作是一个临界区,我们也需要一个角色来管理这个临界区,我们定义一个类Allocator,它有两个重要的功能,分别是同时申请资源apply()和同时释放资源free()。账户Account在执行转账操作的时候,首先向Allocator同时申请转出账户和转入账户这两个资源,成功后再锁定这两个资源,当转账操作执行完后释放锁。

class Allocator {
  private List<Object> als =
    new ArrayList<>();
  // 一次性申请所有资源
  synchronized boolean apply(
    Object from, Object to){
    if(als.contains(from) ||
         als.contains(to)){
      return false;  
    } else {
      als.add(from);
      als.add(to);  
    }
    return true;
  }
  // 归还资源
  synchronized void free(
    Object from, Object to){
    als.remove(from);
    als.remove(to);
  }
}

class Account {
  // actr应该为单例
  private Allocator actr;
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    // 一次性申请转出账户和转入账户,直到成功
    while(!actr.apply(this, target))
      ;
    try{
      // 锁定转出账户
      synchronized(this){              
        // 锁定转入账户
        synchronized(target){           
          if (this.balance > amt){
            this.balance -= amt;
            target.balance += amt;
          }
        }
      }
    } finally {
      actr.free(this, target)
    }
  } 
}
2.破坏不可抢占条件

破坏不可抢占条件核心就是能够主动释放它占有的资源,这一点就不能使用synchronized了,因为synchronized再申请资源的时候,如果申请不到资源就会进入阻塞状态,就不能释放资源了。可以使用java.util.concurrent这个包下提供的Lock就可以解决了。

3.破坏循环等待条件

破坏这个条件,需要对资源进行排序,然后按需申请资源,简单的实现逻辑如下:

class Account {
  private int id;
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    Account left = this        ①
    Account right = target;    ②
    if (this.id > target.id) { ③
      left = target;           ④
      right = this;            ⑤
    }                          ⑥
    // 锁定序号小的账户
    synchronized(left){
      // 锁定序号大的账户
      synchronized(right){ 
        if (this.balance > amt){
          this.balance -= amt;
          target.balance += amt;
        }
      }
    }
  } 
}

学习来源:极客时间 《Java 并发编程实战》学习笔记 Day03

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170232.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python - 数据容器set(集合)

目录 集合的定义 集合的常用操作 添加新元素 add 移除元素 remove 从集合中随机取出元素 pop 清空集合 clear 取出2个集合的差集 difference 消除2个集合的交集 difference_update 2个集合合并 union for循环遍历 set的实用应用 集合的定义 不支持元素的重复&#…

软件设计师学习笔记-程序设计语言基础知识

前言 备战2023年5月份的软件设计师考试&#xff0c;在此记录学习之路。 知识点总结&#xff0c;具体内容请查看对应的模块。 提示&#xff1a;这里有软件设计师资料&#xff0c;包含软件设计师考试大纲、软件设计师第五版官方教程、历年考试真题。 通过百度网盘分享的文件&am…

好好学习,天天向上——“C”

各位uu们我又来啦&#xff0c;今天小雅兰来给大家分享一个有意思的东西&#xff0c;是为&#xff1a;天天向上的力量 基本问题&#xff1a;持续的价值 一年365天&#xff0c;每天进步1%&#xff0c;累积进步多少呢&#xff1f; 1.01^365 一年365天&#xff0c;每天退步1%&#…

python(运算符,顺序,选择,循环语句)

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 python基础语法2前言一、输入输出1.通过控制台输…

CSS 伪类

CSS 伪类 CSS 伪类是添加到选择器的关键字&#xff0c;用于指定所选元素的特殊状态。例如&#xff0c;伪类 :hover 可以用于选择一个按钮&#xff0c;当用户的指针悬停在按钮上时&#xff0c;设置此按钮的样式。 举例说明: button:hover {color: blue; }伪类由冒号&#xff…

【应用】SpringBoot -- Webflux + R2DBC 操作 MySQL

SpringBoot -- Webflux R2DBC 操作 MySQLWebflux 概述Webflux 基本使用Webflux R2DBC 操作 MySQLController ServiceRoute Handler参考文章Webflux 概述 简单来说&#xff0c;Webflux 是响应式编程的框架&#xff0c;与其对等的概念是 SpringMVC。两者的不同之处在于 Webf…

贪心策略(四)合并区间合集

目录 435. 无重叠区间移除元素的最小个数 无重叠区间 区间组合结果 合并区间_牛客题霸_牛客网 435. 无重叠区间 移除元素的最小个数 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重…

【Linux调试器-gdb使用】

目录 1. 背景 2. 使用 3 命令总结 1. 背景 通过c语言的学习我们知道程序的发布方式有两种&#xff0c;debug模式和release模式&#xff0c;debug模式就是我们所说的调试模式。我们已经熟悉了在Windows平台下VS系列的调试&#xff0c;接下来我们一起在无图形化界面的Linux下来…

2023-01-18 flink 11.6 时间水印 和 窗口周期的关系计算方法

forBoundedOutOfOrderness 和 TumblingEventTimeWindowsforBoundedOutOfOrderness&#xff08;M&#xff09;TumblingEventTimeWindows&#xff08;N&#xff09;第一条数据的时间TS1第一个窗口期公式&#xff1a;窗口开始时间&#xff1a;win_start ((TS1-M)/N) * N窗口结束时…

Docker上部署goweb项目

文章目录一、安装go语言环境①下载go语言环境②解压go语言环境到指定目录③验证是否成功④配置镜像加速二、go语言项目配置第一种&#xff1a;先编译后打包&#xff08;分步部署&#xff0c;靠谱&#xff09;第二种&#xff1a;直接打包法三、成功运行一、安装go语言环境 ①下…

Zabbix 监控 Linux操作系统的监控指标

一、Zabbix 监控 Linux操作系统的监控指标 (仅供参考) Zabbi x默认使用Zabbix agent监控操作系统,其内置的监控项可以满足系统大部分的指标监控,因此,在完成Zabbix agent的安装后,只需在前端页面配置并关联相应的系统监控模板就可以了。 如果内置监控项不能满足监控需求…

走向开放世界强化学习、IJCAI2022论文精选、机器人 RL 工具、强化学习招聘、《强化学习周刊》第73期...

No.73智源社区强化学习组强化学习周刊订阅《强化学习周刊》已经开启“订阅功能”&#xff0c;扫描下面二维码&#xff0c;进入主页&#xff0c;选择“关注TA”&#xff0c;我们会向您自动推送最新版的《强化学习周刊》。本期贡献者&#xff1a;&#xff08;李明&#xff0c;刘青…

【Kotlin】类的继承 ① ( 使用 open 关键字开启类的继承 | 使用 open 关键字开启方法重写 )

文章目录一、使用 open 关键字开启类的继承二、使用 open 关键字开启方法重写一、使用 open 关键字开启类的继承 Kotlin 中的类 默认都是 封闭的 , 无法被继承 , 如果要想类被继承 , 需要在定义类时 使用 open 关键字 ; 定义一个普通的 Kotlin 类 : class Person(val name: S…

分析GC日志来进行JVM调优

不同的垃圾收集器产生的GC日志大致遵循了同一个规则&#xff0c;只是有些许不同&#xff0c;不过对于G1收集器的GC日志和其他垃圾收集器有较大差别&#xff0c;话不多说&#xff0c;正式进入正文。。。 什么时候会发生垃圾收集 首先我们来看一个问题&#xff0c;那就是什么时…

SpringBoot集成Elasticsearch7.4 实战(三)

本篇文章主要讲的是:在Springboot环境下&#xff0c;管理数据1. 数据管理1.1. 新增数据1.1.1. 单实例新增http方式提交数据&#xff0c;案例中我将数据格式做了规范&#xff0c;提交过程中需要指定索引名&#xff0c;以及JSON格式数据&#xff0c;这样尽可能在实际过程中做到通…

图论算法:普里姆算法(C++实现+图解)

文章目录最小生成树普里姆算法实现过程代码实现最小生成树 什么是最小生成树? 对于如图所示的带权无向连通图来说&#xff1a;从图中任意顶点出发&#xff0c;进行dfs或者bfs便可以访问到图中的所有顶点&#xff0c;因此连通图中一次遍历所经过的边的集合以及图中所有顶点的…

libvirt零知识学习2 —— libvirt源码下载

1. libvirt官网主页 libvirt的官网地址为&#xff1a; https://libvirt.org/ 主页如下图所示&#xff1a; 2. libvirt官网下载主页 libvirt的官网下载页地址为&#xff08;在主页中点击“Download”按钮即可跳转到&#xff09;&#xff1a; https://libvirt.org/downloads…

KaiwuDB 首席解决方案专家 金宁:1.0 时序数据库核心功能解读

以下是实录文章精简版欢迎大家点赞、收藏、关注&#xff01;大家好&#xff0c;今天介绍将分为 3 部分&#xff1a;首先从物联网蓬勃发展的时代背景出发&#xff0c;我们一起来看看数据库究竟将面临怎样的挑战与机遇&#xff1b;接着我将为大家详细 KaiwuDB 1.0 时序数据库的核…

(Java高级教程)第四章必备前端基础知识-第一节:HTML

文章目录一&#xff1a;HTML概述&#xff08;1&#xff09;概述&#xff08;2&#xff09;标签&#xff08;3&#xff09;HTML基本结构二&#xff1a;常用标签介绍&#xff08;1&#xff09;注释&#xff08;2&#xff09;标题&#xff08;3&#xff09;段落&#xff08;4&…

React Fragment

首先 我们编写这样一个例子 我们在创建一个react项目 在src的目录下创建components目录 components下创建一个子组件 我这里的名字叫 subset.jsx import React from "react";export default class subset extends React.Component{constructor(props){super(prop…