当前快报:一篇文章告诉你什么是Java内存模型

2023-05-23 09:25:23 来源:博客园 分享到:

在上篇 并发编程Bug起源:可见性、有序性和原子性问题,介绍了操作系统为了提示运行速度,做了各种优化,同时也带来数据的并发问题,

定义

在单线程系统中,代码按照顺序从上往下顺序执行,执行不会出现问题。比如一下代码:

int a = 1;int b = 2;int c = a + b;

程序从上往下执行,最终c的结果一定会是3


(相关资料图)

但是在多线程环境中,代码就不一定会顺序执行了。代码的运行结果也有不确定性。在开发中,自己本地没问题,一行行查看代码也没有问题,但是在高并发的生产环境就会出现违背常理的问题。

多线程系统提升性能有如下几个优化:

单核的cpu改成多核的cpu,每个cpu都有自己的缓存。多个线程可以在cpu线程切换。代码可能根据编译优化,更新代码的位置。

这些优化会导致可见性原子性以及有序性问题,为了解决上述问题,Java内存模型应运而生。

Java内存模型是定义了Java程序在多线程环境中,访问共享内存和内存同步的规范,规定了线程之间的交互方式,以及线程与主内存、工作内存的的数据交换。

Java内存模型解决并发

导致可见性的原因的是缓存,导致有序性的问题是编译优化,那解决可见性、有序性问题就是禁用缓存和编译优化。这样虽然解决了并发问题,但是性能却下降了。

合理的方案就是按需求禁用缓存和编译优化,在需要的地方添加对应的编码即可。Java内存模型规范了JVM如何按需禁用缓存和编译优化,具体包括volatilesynchronizedfinal这几个关键字,以及Happens-Before规则。

可见性问题

在多核cpu操作系统中每次cpu都有自己的缓存,cpu先从内存获取数据,再进行运算。比如下图中线程A和线程B,分别运行自己的cpu,然后从内存获取变量到自己的cpu缓存中,并进行计算。

线程B改变了变量之后,线程A是无法获取到最新的值。以下代码中,启动两个线程,线程启动完线程A,循环获取变量,如果是true,一直执行循环,直到被改成false才跳出循环,然后再延迟1s启动线程B,线程修改变量值为true:

private static boolean flag = true;// 线程A一直读取变量flag,直到变量为false,才跳出循环class ThreadA extends Thread {    @Override    public void run() {        while (flag) {            // flag 为 true,一直读取flag字段,flag 为 false 时跳出来。            //System.out.println("一直在读------" + flag);        }        System.out.println("thread - 1 跳出来了");    }}// 1s 后线程B将变量改成 falseclass ThreadB extends Thread {    @Override    public void run() {        System.out.println("thread-2 run");        flag = false;        System.out.println("flag 改成 false");    }}@Testpublic void test2() throws InterruptedException {    new Thread1().start();    // 暂停一秒,保证线程1 启动并运行    Thread.sleep(1000);    new Thread2().start();}

运行结果:

thread-2 runflag 改成 false

线程A一直处于运行中,说明线程B修改后的变量,线程A并未知道。

flag变量添加volatile声明,修改成:

private static volatile boolean  flag = true;

再运行程序,运行结果:

thread-2 runflag 改成 falsethread - 1 跳出来了

线程B运行完后,线程A也跳出了循环。说明修改了变量后,其他线程也能获取最新的值。

一个未声明volatile的变量,都是从各自的cpu缓存获取数据,线程更新数据之后,其他线程无法获取最新的值。而使用volatile声明的变量,表明禁用缓存,更新数据直接更新到内存中,每次获取数据都是直接内存获取最新的数据。线程之间的数据都是相互可见的。

可见性来自happens-before规则,happens-before用来描述两个操作的内存可见性,如操作Ahappens-before操作B,那么A的结果对于B是可见的,前面的一个操作结果对后续操作是可见的happens-before定义了以下几个规则:

解锁操作happens-before同一把锁的加锁操作。volatile 字段的写操作happens-before同一字段的读操作。线程的启动操作happens-before该线程的第一个操作。Ahappens-beforeB,且Bhappens-beforeC,那么Ahappens-beforeC。happens-before具有传递性。有序性问题

先看一个反常识的例子:

int a=0, b=0;public void method1() {    b = 1;    int r2 = a; }public void method2() {    a = 2;     int r1 = b; }

定义了两个共享变量ab,以及两个方法。第一个方法将共享变量b赋值为1,然后将局部变量r2赋值为a。第二个方法将共享变量a赋值为2,然后将局部变量r1赋值为b

在单线程环境下,我们可以先调用第一个方法method1,再调用method2方法,最终得到r1r2的值分别为1,0。也可以先调用method2,最后得到r1r2的值分别为0,2

如果代码没有依赖关系,JVM编译优化可以对他们随意的重排序,比如method1方法没有依赖关系,进行重排序:

int a=0, b=0;public void method1() {    int r2 = a;     b = 1;}public void method2() {     int r1 = b;     a = 2;}

此时在多线程环境下,两个线程交替运行method1method2方法:

重排序后r1r2分别是0,0

那如何解决重排序的问题呢?答案就是将变量声明为volatile,比如a或者b变量声明volatile。比如b声明为volatile,此时b的赋值操作要happens-beforer1的赋值操作。

int a=0;volatile int b=0;public void method1() {    int r2 = a;     b = 1;}public void method2() {     int r1 = b;     a = 2;}

同一个线程顺序也满足happens-before关系以及传递性,可以得到r2的赋值happens-beforea的赋值。也就表明对a赋值时,r2已经完成赋值了。也就不可能出现r1r200的结果。

内存模型的底层实现

Java内存模型是通过内存屏障来实现禁用缓存和和禁用重排序

内存屏障会禁用缓存,在内存写操作时,强制刷新写缓存,将数据同步到内存中,数据的读取直从内存中读取。

内存屏障会限制重排序操作,当一个变量声明volatile,它就插入了一个内存屏障,volatile字段之前的代码只能在之前进行重排序,它之后的代码只能在之后进行重排序。

总结

Java内存模型(Java Memory Model,JMM)定义了Java程序中多线程之间共享变量的访问规则,以及线程之间的交互行为。它规定了线程如何与主内存和工作内存交互,以确保多线程程序的可见性、有序性和一致性。

可见性:使用volatile声明变量,数据读取直接从内存中读取,更新也是强制刷新缓存,并同步到主内存中。

有序性:使用volatile声明变量,确保编译优化不会重排序该字段。

Happens-Before: 前面一个操作的结果对后续操作是可见的

参考

Java内存模型

Java内存模型:看Java如何解决可见性和有序性问题

标签:

当前快报:一篇文章告诉你什么是Java内存模型

来源:博客园 2023-05-23 09:25:23

简单学习网专用学习机_简单学习网

来源:互联网 2023-05-23 09:11:19

曝曼联真要签内马尔!已对话巴黎 今夏将清洗马夏尔|当前滚动

来源:网易体育 2023-05-23 08:24:52

【当前热闻】一通电话接通后怎样不让对方挂机 通话中怎么不让别人打进来

来源:互联网 2023-05-23 07:33:17

国海证券给予箭牌家居增持评级

来源:每日经济新闻 2023-05-23 06:33:32

中国光大银行(06818.HK):5月22日南向资金增持123.8万股-时讯

来源:证券之星 2023-05-23 05:43:27

苦瓜怎么吃才能降血糖怎么快速降血糖 苦瓜怎么吃才能降血糖

来源:城市网 2023-05-23 03:16:45

小荷才露尖尖角描写什么季节_小荷才露尖尖角 全球热消息

来源:互联网 2023-05-23 00:37:45

看今晚黄金选择新方向;1970直接多

来源:汇通财经 2023-05-22 22:14:14

新资讯:澜起科技股东嘉兴宏越及其一致行动人合计减持1102.28万股 套现7.34亿

来源:挖贝网 2023-05-22 21:37:33

全球头条:新视觉影院hd高清_新视觉影

来源:互联网 2023-05-22 20:52:24

好看小说推荐双男主_好看小说推荐女生看

来源:互联网 2023-05-22 20:10:08

雇主不吃饺子,保姆拿回家自己吃,结果全家人都吃坏了肚子

来源:社会面面观察 2023-05-22 19:10:41

贵州举办第三十三次全国助残日系列活动 播资讯

来源:新华网 2023-05-22 18:33:24

恩捷股份:公司锂电池铝塑膜产业化项目将加强公司在软包电池领域的布局,丰富公司产品线 世界最新

来源:同花顺 2023-05-22 17:52:45

今日快讯:燃!定襄首届“双镇”特色文化节·摇滚音乐现场回眸

来源:黄河新闻网忻州频道 2023-05-22 17:22:48

天天微速讯:央企主题ETF实现超额募集;电力ETF大涨逾3%;主动权益定开基金开放日迎大考

来源:每日经济新闻 2023-05-22 17:00:30

广电网络: 关于召开2022年度暨2023年第一季度业绩说明会的公告

来源:证券之星 2023-05-22 16:18:59

万宁一岁半女童离家30米附近失踪已40多个小时 家属:周边搜寻了五六公里也没找到

来源:海报新闻 2023-05-22 16:01:25

深圳市信宇人上市,拟募资4.62亿元

来源:世纪储能 2023-05-22 15:12:21

德媒调查:“北溪”爆炸新线索指向乌克兰军方 环球播报

来源:参考消息 2023-05-22 14:49:33

联名新式茶饮 19元就能体验的高奢品牌“走下神坛”? 当前快报

来源:中国网财经 2023-05-22 13:49:44

壁纸美图分享第(121)期 - 水着篇21_世界即时看

来源:哔哩哔哩 2023-05-22 13:14:02

头条焦点:暴雨来袭,广西桂林371所学校停课

来源:界面新闻 2023-05-22 12:37:33

当前速递!打响新能源下乡第一枪,五菱宏光MINIEV家族至高直降13000元,2.98万元起

来源:搜狐汽车 2023-05-22 11:56:20

头条:教育部:中小学不得通过考试或变相考试选拔学生

来源:中国网财经 2023-05-22 11:20:24

梅西要来北京了!6月15日,阿根廷与澳大利亚在京进行友谊赛 世界速读

来源:环球网 2023-05-22 11:16:30

天天最资讯丨祥鑫科技(002965):股价成功突破年线压力位-后市看多(涨)(05-22)

来源:自选股智能写手 2023-05-22 10:56:48

世界通讯!20GWh磷酸铁锂储能电池项目一期将于8月正式投产

来源:北极星电池网 2023-05-22 10:14:25

喂养湖羊 增收致富 环球焦点

来源:驻马店网 2023-05-22 09:46:57

Copyright   2015-2022 北冰洋知识产权网 版权所有  备案号:沪ICP备2020036824号-3   联系邮箱:562 66 29@qq.com