并发编程的故事——共享模型之内存

共享模型之内存

文章目录

  • 共享模型之内存
  • 一、JVM内存抽象模型
  • 二、可见性
  • 三、指令重排序


一、JVM内存抽象模型

主要就是把cpu下面的缓存、内存、磁盘等抽象成主存和工作内存
体现在
可见性
原子性
有序性

二、可见性

出现的问题
t线程如果频繁读取一个静态变量,那么JIT编译器会把它存入到线程的缓存,那么就算主线程修改了主存中的静态变量也没有任何作用,因为t线程读取的是缓存里面的。所以程序判断仍然是错误无法停止。

@Slf4j(topic = "c.Test32")
public class Test32 {// 易变static boolean run = true;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(true){if(!run) {break;}}});t.start();sleep(1);run = false; // 线程t不会如预想的停下来}
}

在这里插入图片描述
解决方案
volatile和synchronized可以让线程不能访问缓存,一定要访问主内存里面的run。

@Slf4j(topic = "c.Test32")
public class Test32 {// 易变static boolean run = true;static Object lock=new Object();public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(true){synchronized (lock){if(!run) {break;}}}});t.start();sleep(1);synchronized (lock){run = false; }}
}

加上sout也是可以解决可视化问题。原因是这个println是一个synchronize的方法,也就是要输出那么就会在同步块,同步块可以完成可视化,那么自然run就可以被读取。

public void println(boolean x) {synchronized (this) {print(x);newLine();}}

三、指令重排序

为什么要指令重排?
因为各个语句都是由多个指令组成,相当于是多个分工,这些分工有的可以同时完成,那么就把他们先组合到一起。其它需要前一步的结果的指令就在后面排序等待。

诡异的结果
这里其实就是指令重排会导致这个结果是0。实际上就是线程2指令重排先执行了ready=true,然后被切换到线程1,刚好通过if先做出了计算,最后才是切换到线程2执行num=2
在这里插入图片描述
解决
使用volatile可以防止变量前面的代码重排序
在这里插入图片描述
volatile原理
volatile的原理其实就是内存屏障。写屏障就是把修改的变量之前的所有变量同步到主存中每次都是在主存中修改,而且防止前面的代码指令重排到屏障之后。如果是读屏障那么就是带有volatile变量以下的所有变量都同步到主存中,防止屏障以下的代码重排到屏障之前,也就保护了volatile属性。
保证了写屏障的变量是最新的
但是无法解决指令交错问题,也就是只能在本地线程保证指令有序,但是无法保证多线程的指令交错问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
双重检查
单例模式为了防止多次加锁,可以先判空之后,再加锁,再判空。这样的好处就是创建对象之后,只需要判空,而不需要再次加锁。只有在第一次需要加锁创建对象,防止多个线程同时创建对象。
问题
第一个if代码会被指令重排,为什么会重排?
在这里插入图片描述
双重检查的问题根源分析(dcl)
关键就是if(INSTANCE==null)是一个在monitor之外的代码,那么产生的问题就是在执行INSTANCE=new Singleton()的时候,他并不是一个原子操作,包括了invokespecial执行构造方法指令和putstatic给引用赋值(找到对象的堆内存地址)
补充:那么这里synchronize为什么还是会出现指令重排?
原因是它本来就会产生指令重排,只不过在synchronize中不会产生原子化,可视化和有序化的问题,但这里是两个线程而且synchronize没有完全控制变量INSTANCE的原因。
在这里插入图片描述
在这里插入图片描述
解决方案
可以通过volatile的读写屏障防止代码指令重排到屏障之外,这样就能够避免invokespecial走到putstatic后。
在这里插入图片描述
在这里插入图片描述
happen-before(可见性)
synchronize
volatile
等待线程执行完之后再读取变量
静态变量写好之后,线程才调用
线程打断之前的修改
变量默认值
在这里插入图片描述
在这里插入图片描述
习题
balking习题
指令重排序问题,解决方案可以使用synchronize来把这些变量框住,防止其它线程切换的时候都通过了第一个if,导致重复执行问题
在这里插入图片描述
在这里插入图片描述
1、为什么加上final
原因就是防止类被继承,之后重写的方法带上单例对象被改变
2、怎么防止反序列化破坏单例
需要增加一个返回Obj的方法,直接返回单例对象,而不是通过字节码重新创建
3、为什么构造私有化
防止被创建很多次
4、初始化能保证线程安全吗
静态变量在类加载的时候完成了初始化
5、不把Instance变成public的原因
防止直接被修改,提供封装性,隐藏细节

在这里插入图片描述
1、字节码里面全部都是public final static的类对象,所以可以限制实例对象
2、不会有并发问题,在类加载的时候静态变量已经加载完了
3、不会被反射破坏单例,enum的设计
4、也不会被反序列化破坏,它实现了序列化和返回单例的方法
5、它是一个饿汉式
在这里插入图片描述
在这里插入图片描述
总结
可见性(jvm优化速度,把变量放进线程的缓存)
有序性(指令重排,优化执行速度)
happen-before写入是否对线程可见
volatile原理
同步模式balking


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

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

相关文章

高精度地图定位在高速公路自动驾驶系统中的应用

近年来随着汽车保有量不断增加,随之而来的是: ( 1) 严重的交通拥堵,通行效率低下,用在通行上的时间不断增加; ( 2) 交通事故频发,交通事故导致的伤亡人数和费用不断增加,而且绝大多数事故是由人为因素导致的; ( 3) 大气…

算法——排序

排序 下面的代码会用到宏定义,因为再C中没有swap交换函数,所以对于swap的宏定义代码如下: #define swap(a, b) {\__typeof(a) __a a; a b; b __a;\ } 稳定排序: 1.插入排序: 插入排序会将数组,分位两个部…

GPU编程(基于Python和CUDA)(二)——显示GPU信息

系列文章目录 GPU编程(基于Python和CUDA)(一)——零基础安装pycuda GPU编程(基于Python和CUDA)(二)——显示GPU信息 显示GPU信息 系列文章目录前言通过CUDA查看GPU信息使用pycuda查…

CA证书颁发机构服务器

目录 一、CA证书颁发机构是什么? 二、数字证书可以干什么? 三、PKI:即公钥加密体系(public key cryptography) 四、CA在网络中的工作流程及原理(以网站为例) 五、HTTPS 的工作原理 六、CA私有证…

关于CICD流水线的前端项目运行错误,npm项目环境配置时出现报错:Not Found - GET https://registry.npm...

关于CICD流水线的前端项目运行错误,npm项目环境配置时出现报错:Not Found - GET https://registry.npm… 原因应该是某些jar包缓存中没有需要改变镜像将包拉下来 npm config set registry http://registry.npm.taobao.org npm install npm run build

Android 下第一个fragment app 先Java 后Kotlin

看着视频学习的,Fragment:3.Fragment使用方法_哔哩哔哩_bilibili 在android studio 下新建一个工程,类型是 Empty View Activity,本身就有一个Activity。就有文件MainActivity.java 或者kt,还有一个layout 文件&#…

$attrs,$listeners

vue实现组件通信的方式有: 父子通信 父组件向子组件传递通过props定义各个属性来传递,子组件向父组件传递通过$emit触发事件 ref也可以访问组件实例跨级通信 vuex bus provide / inject $attrs / $listeners解释 $attrs / $listeners $attrs 将父组件中…

linux免密登录报错 Bad owner or permissions on /etc/ssh/ssh_config.d/05-redhat.conf

问题:权限不对的 解决: 1.检查文件的所有者和权限。 确保文件的所有者是正确的。 运行以下命令来确定文件的所有者和权限: ls -l /etc/ssh/ssh_config.d/05-redhat.conf 通常情况下,SSH配置文件应该属于root用户。如果所有者不是…

前端list列表自定义图标并设置大小

前端list列表自定义图标并设置大小 一、前端list列表基础知识回顾 前端公有两种列表,一种是有序列表(ol),一种是无序列表(ul),它们的子元素都是(li)。 1.1 有序列表&a…

模拟电子技术基础学习笔记三 PN结

采用不周的掺杂工艺,将P型半导体与N型半导体制作在同一块硅片上,在它们的交界面就形成PN结。 扩散运动 物质总是从浓度高的地方向浓度低的地方运动,这种由于浓度差而产生的运动称为扩散运动。 空间电荷区 - 耗尽层 漂移运动 在电场力的作…

(数学) 剑指 Offer 39. 数组中出现次数超过一半的数字 ——【Leetcode每日一题】

❓ 剑指 Offer 39. 数组中出现次数超过一半的数字 难度:简单 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输…

语音专线如何接入呼叫中心系统

想要了解语音专线是否可以接入呼叫中心系统,首先要分别了解什么是语音专线和什么是呼叫中心系统。语音专线接入呼叫中心系统想要实现什么功能,下面小易就来科普一下。 什么是语音专线?语音专线可以理解为联通、电信、移动运营商提供的一种语音…

并发编程的故事——Java线程

Java线程 文章目录 Java线程一、线程创建二、线程运行三、线程运行四、主线程和守护线程五、线程的五种状态六、线程的六种状态七、烧水泡茶案例 一、线程创建 创建线程方法一: Thread重写run方法 Slf4j(topic "c.MyTest1") public class MyTest1 {publ…

HTML 播放器效果

效果图 实现代码 <!DOCTYPE HTML> <html><head><title>爱看动漫社区 | 首页 </title><link href"css/bootstrap.css" relstylesheet typetext/css /><!-- jQuery --><script src"js/jquery-1.11.0.min.js"…

Mysql表关联简单介绍(inner join、left join、right join、full join不支持、笛卡尔积)

文章目录 0. 交集、并集、差集含义说明1. 简单演示上图七种情况0. A、B表数据准备1. left outer join 简称 left join 左表所有数据&#xff0c;右表关联数据&#xff0c;没有的以null填充2. right outer join 简称 right join&#xff0c;右表所有数据&#xff0c;左表关联数据…

【java中的Set集合】HashSet、LinkedHashSet、TreeSet(最通俗易懂版!!)

目录 一、HashSet集合 1.HashSet集合的特点 2.HashSet常用方法 二、LinkedHashSet集合 LinkedHashSet集合的特点 三、TreeSet集合 1.TreeSet集合的特点 2.TreeSet的基本使用 四、HashSet、LinkedHashSet、TreeSet的使用场景 五、list和set集合的区别 一、HashSet集合 …

【Apollo学习笔记】——规划模块TASK之SPEED_BOUNDS_PRIORI_DECIDER

文章目录 前言SPEED_BOUNDS_PRIORI_DECIDER功能简介SPEED_BOUNDS_PRIORI_DECIDER相关配置SPEED_BOUNDS_PRIORI_DECIDER流程将障碍物映射到ST图中ComputeSTBoundary(PathDecision* path_decision)ComputeSTBoundary(Obstacle* obstacle)GetOverlapBoundaryPointsComputeSTBounda…

Apache SeaTunnel 2.3.3 版本发布,CDC 支持 Schema Evolution!

时隔两个月&#xff0c; Apache SeaTunnel 终于迎来大版本更新。此次发布的 2.3.3 版本在功能和性能上均有较大优化改进&#xff0c;其中大家期待已久的 CDC Schema evolution&#xff08;DDL 变更同步&#xff09;、主键 Split 拆分、JDBC Sink 自动建表功能、SeaTunnel Zeta …

4.6 TCP面向字节流

TCP 是面向字节流的协议&#xff0c;UDP 是面向报文的协议 操作系统对 TCP 和 UDP 协议的发送方的机制不同&#xff0c;也就是问题原因在发送方。 UDP面向报文协议&#xff1a; 操作系统不会对UDP协议传输的消息进行拆分&#xff0c;在组装好UDP头部后就交给网络层处理&…

C++之访问vector<vector<char>>中的vector<char>元素(一百八十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…