多线程04 死锁,线程可见性

前言

前面我们讲到了简单的线程安全问题以及简单的解决策略

其根本原因是cpu底层对线程的抢占式调度策略,随机调度

其他还有一些场景的问题如下

1.多个线程同时修改一个变量问题

2.执行的操作指令本身不是原子的

比如自增操作就分为三步,加载,自增,保存

3.内存可见性问题

4.指令重排序问题

下面两个问题将会在本文中被解决

前面我们说到了解决几个线程同时修改一个变量的问题,我们使用加锁的方式来解决

使用synchronized关键字

特殊用法:用synchronized修饰普通方法,此时同步监视器就变为了this

修饰静态方法的时候此时相当于使用类对象当做同步监视器

synchronized加的锁也可以称为互斥锁

1.synchronized的一些其他特性

先举个例子

public class ThreadDemo21 {public static void main(String[] args) {Object lock = new Object();Thread t1 = new Thread(()->{synchronized (lock){synchronized (lock){System.out.println("hello");}}});t1.start();}
}

这里我们直观上感觉,t1先持有了这个lock锁,此时在没有释放的情况下再进行加锁理论上应该会出现阻塞的情况,但是实际上并没有阻塞.这里的线程是会正确执行的?

为什么呢???

这是因为这里的两次加锁是同一个线程进行的,所以第二次锁实际上并没有添加,只是真正加了一次锁,第二次加锁实际上是以计数器的形式自增一次,而并没有真正的加锁,所以释放的时候也释放了一次.

有人问这有啥用呢???

其实是为了我们在写一些复杂逻辑的代码中可能会忘了这些加锁的过程,从而导致以上的阻塞的情况(称为死锁)

这个时候其实就巧妙的解决了问题,比如说如下情况

此时这种锁的机制称为"可重入锁"的机制

2.三种经典的死锁场景

1.一个线程一把锁

也就是我们刚刚讨论的场景,如果这个时候锁没有这个"可重入锁"的机制,我们就会发生死锁问题.

2.两个线程两把锁

举个例子,这里假设线程1拿到a锁,想获取b锁,同时线程2拿到b锁想获取a锁,此时两者都在等另一个线程释放另一个锁,就发生了僵持的效果

你可以想象两者发生一个交易,一个想先交钱,一个想先交货,两个人一直僵持而迟迟不能完成交易.

public class ThreadDemo23 {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {Thread t1 = new Thread(()->{synchronized (lock1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (lock2){System.out.println("t1我拿到了两个锁");}}});Thread t2 = new Thread(()->{synchronized (lock2){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (lock1){System.out.println("t1我拿到了两个锁");}}});t1.start();t2.start();}
}

此时加上两个sleep是因为,希望在获取对应锁执行希望对应的一方获取到了对应的锁,此时执行就会发生僵持的效果

上述想解决僵持效果只需要将其中的一个线程的获取锁顺序的

3.n个线程m把锁

这里就涉及到一个哲学家进餐的问题

由Dijkstra提出并解决的哲学家就餐问题是典型的同步问题。该问题描述的是五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替的进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续思考。

这个时候,加入五个人同时想进餐,这个时候就会发生每个人都拿到一只筷子,而不愿意放下,这就构成了一个死锁

谈解决方案之前,我们要先讨论一下构成死锁的四个必要条件

1.互斥使用,使用锁的过程是互斥的,一个线程拿到这个锁就,另一个线程想要获取就得阻塞等待

2.不可抢占 一个线程获取这个锁,只能等其他的线程主动释放

3.请求保持 持有a获取b

4.环路等待

这里1和2都不太容易破坏,只有3和4方便破坏

3可能是代码业务逻辑需求的

所以此时修改4是最合理的

此时想解决这个问题,提出几个思路

1.去掉一个哲学家

2.增加一支筷子

3.引入计数器,限制同时可以支持多少个人一起吃吃面

4.引入加锁的规则(较为常用,这里就可以控制获取筷子的顺序,此时给筷子排上编号,只能先获取编号小的筷子,此时2号获取了筷子1,以此类推,最后5获取了两个筷子,最后他结束了,其他线程/哲学家就可以吃到饭了)

5.银行家算法(太过复杂,一般不用)

3.内存可见性问题

老样子,先举个例子

public class ThreadDemo22 {private  static  int flag = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{while(flag == 0){}System.out.println("线程结束!");});Thread t2 = new Thread(()->{System.out.println("请输入一个数字");Scanner sc = new Scanner(System.in);flag = sc.nextInt();});t1.start();t2.start();}
}

此时我们想进行修改flag为任何值都发现是不成功的

这是因为,flag == 0这个操作分为两个指令

1.从内存中读取flag的值到寄存器中

2.读取完和0进行比较,然后进行一个跳转

在我们输入这个数字之前,其实这个while循环已经实现了很多次了

在这两个指令中,比较是没有多大开销的,然而从内存中加载的开销是比较大的

JVM认为这么多次这个变量始终没有修改,为了提高效率,直接把这个加载的动作直接优化掉了

其实可以理解为JVM的一个bug,此时我们可以使用sleep(n)让这个加载的频率降低,这样就不会优化了.但是治标不治本我们这里引入一个新的关键字volatile

用这个关键字来修饰flag就会让其强制读取内存,这样的结果就会更精确!!

网上还有一个说法就是将这里的内存(缓存)和寄存器的概念换成了"主存"和"工作内存"的概念,显得更严谨.

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

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

相关文章

9.增删改操作

目录 一、插入操作 1、为表的所有字段插入数据 2、为表的指定字段插入数据 3、同时插入多条记录 4、将查询结果插入表中: 二、更新操作 三、删除操作 四、练习题 一、插入操作 在使用数据库之前,数据库中必须要有数据,MYSQL中使INSE…

【李宏毅-元学习】

一、基本概念 1、元学习:学习如何学习,超参数调整 2、机器学习和元学习 机器学习:定义函数(未知参数)-定义损失函数-优化(最小化损失函数) 3、什么是元学习 机器学习通过三个步骤找到了学习算…

【Web】PHP反序列化刷题记录

目录 ①[NISACTF 2022]babyserialize ②[NISACTF 2022]popchains ③[SWPUCTF 2022 新生赛]ez_ez_unserialize ④[GDOUCTF 2023]反方向的钟 再巩固下基础 ①[NISACTF 2022]babyserialize <?php include "waf.php"; class NISA{public $fun"show_me_fla…

pycharm 怎么切换Anaconda简单粗暴

&#xff08;1&#xff09;创建一个环境 &#xff08;2&#xff09;选择一下自己conda的安装路径中conba.exe (3)选择存在的环境&#xff0c;一般会自动检测到conda创建有哪些环境&#xff0c;导入就行

蓝桥杯刷题day01——字符串中的单词反转

题目描述 你在与一位习惯从右往左阅读的朋友发消息&#xff0c;他发出的文字顺序都与正常相反但单词内容正确&#xff0c;为了和他顺利交流你决定写一个转换程序&#xff0c;把他所发的消息 message 转换为正常语序。 注意&#xff1a;输入字符串 message 中可能会存在前导空…

使用Docker compose方式安装Spug,并结合内网穿透实现远程访问

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件…

Leetcode—907.子数组的最小值之和【中等】

2023每日刷题&#xff08;四十二&#xff09; Leetcode—907.子数组的最小值之和 算法思想 参考自y神思想 实现代码 class Solution { public:int sumSubarrayMins(vector<int>& arr) {long long ans 0;const int mod 1e97;int n arr.size();stack<int>…

山寨Stream API

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 一个需求 List<Per…

linux LVM /dev/sdb mount dir /data【linux LVM 磁盘挂载目录】

添加磁盘 /dev/sdb rootregistry01 ~]# fdisk -lDisk /dev/sda: 53.7 GB, 53687091200 bytes, 104857600 sectors Units sectors of 1 * 512 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk lab…

Ubuntu 22.03 LTS 安装deepin-terminal 实现 终端 分屏

deepin-terminal 安装 源里面自带了这个软件&#xff0c;可以直接装 sudo apt install deepin-terminal 启动 按下Win键&#xff0c;输入deep即可快速检索出图标&#xff0c;点击启动 效果 分屏 CtrlShiftH 水平分割 CtrlShiftJ 垂直分割 最多分割成四个小窗口&#xff0…

『VUE3后台—大事件管理系统』

项目地址&#xff1a;https://gitee.com/csheng-gitee/vue3-big-event-admin 技术栈&#xff1a;VUE3 Pinia Pnpm&#xff08;本项目暂不用 typescript&#xff09; 一、前期准备工作 1、创建项目 npm install -g pnpm pnpm create vue2、ESLint 配置 (1) 禁用 prettier 插…

本机putty无法连接到虚拟机中kali操作系统

sudo apt-get install -y openssh-server安装一下软件&#xff0c;我这里已经安装好了&#xff0c;所以没有安装过程了。 firewall-cmd --zonepublic --remove-port22/tcp --permanent想要打开22端口&#xff0c;发现报错如下&#xff1a; Could not find command-not-found…

JAVA进阶之路JVM-1:jvm基本组成、java程序执行过程、java程序的跨平台、静态编译器、jvm执行方式

JVM基本组成 当线上系统突然宕机&#xff0c;系统无法访问&#xff0c;甚至直接OOM&#xff1b; 线上系统响应速度太慢&#xff0c;优化系统性能过程中发现CPU占用过高&#xff0c;原因也许是因为JVM的GC次数过于频繁 因此&#xff0c;新项目上线&#xff0c;需要设置JVM的各…

力扣70 爬楼梯

文章目录 力扣70 爬楼梯示例代码实现总结收获 力扣70 爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 代码实现 class Solution {Map <Integer,Integer> mapnew HashMap();p…

常见的软件测试用例设计方法有哪些?

常见的软件测试用例设计方法&#xff0c;个人认为主要是下面这6种&#xff1a; 1)流程图法&#xff08;也叫场景法&#xff09; 2)等价类划分法 3)边界值分析 4)判定表 5)正交法 6)错误推测法 这6种常见方法中&#xff0c;我分别按照定义、应用场景、使用步骤、案例讲解…

对淘宝购物车进行测试用例设计

当对淘宝的购物车进行测试用例设计时&#xff0c;可以考虑涵盖以下几个方面的测试&#xff1a; 1.添加商品到购物车 测试用例&#xff1a; 1.1 验证用户能否成功将商品添加到购物车1.2 验证用户能否将商品添加到购物车后&#xff0c;购物车内的商品数量是否正确1.3 验证用户能…

vatee万腾的数字探险:Vatee科技创新的未知征程

在科技风潮的巅峰&#xff0c;Vatee万腾如一艘科技探险的航船&#xff0c;勇敢地驶向未知的数字化征程。 Vatee万腾在数字探险的过程中展现出征服未知领域的坚定决心。他们不满足于现状&#xff0c;而是积极地寻找和探索那些尚未被揭示的数字化领域。这种决心使得Vatee能够在科…

量子计算软件平台

目录 1.量子语言 2.量子软件开发工具 3.量子云计算平台 1.量子语言 量子语言是一种基于量子计算机的语言&#xff0c;用于描述和实现量子算法。与经典计算机语言不同&#xff0c;量子语言需要考虑量子力学的特殊规则和算法的量子化。其中&#xff0c;最常用的量子语言是量子程…

基于51单片机的超声波测距系统【程序+proteus仿真+参考论文+原理图+PCB等16个文件夹资料】

一、项目功能简介 整个设计系统由STC89C52单片机LCD1602显示模块声光报警模块存储模块超声波模块按键模块组成。 具体功能&#xff1a; 1、超声波测量距离&#xff0c;显示在LCD1602。 2、存储模块可以存储超声波报警值。 3、通过按键可设置报警值大小。 4、超声波报警距…

ViLT 论文精读【论文精读】

ViLT 论文精读【论文精读】_哔哩哔哩_bilibili 目录 ViLT 论文精读【论文精读】_哔哩哔哩_bilibili 1 地位 2 ViLT做了什么能让它成为这种里程碑式的工作&#xff1f; 3 ViLT到底把模型简化到了什么程度&#xff1f;到底能加速到什么程度&#xff1f; 2.1 过去的方法是怎…