JMM相关知识学习总结(一)

Java Memory Model  

在并发编程中需要处理的两个问题是:线程之间如何同步,以及线程之间如何通信。有两种主流的方案分别是共享内存和消息传递而我们的java采用的就饿时第一种共享内存模型来间接的实现,线程间的通信和同步。为什么需要JMM?

让我们看一个简单的demo:

这段代码的运行结果如下:

可见程序进入了死循环并不能结束,问题出在了哪里呢?我们有两种推测,要么主线程并未将boolean值写入,或者子线程并未读到主线程修改的变化(所以此时我们称出现了可见性问题)

为什么我们并发的程序会出现各种奇怪的问题呢? 首先我们来介绍一下背景计算机在执行程序时,为了提高性能,我们的cpu以及jvm的编译器在执行代码时都会进行一定的优化,先说cpu:我们的cpu处理器主要使用多级缓存和指令序列重拍来提高性能。我们都知道cpu的运行速度要远远快于内存。所以很多时候我们的cpu都在等内存,这样白白浪费了大量的资源。而多级缓存的出现就为了解决这个问题(多核的cpu在同时处理主内存中的一个值时会出现一致性问题。cpu通过使用mesi协议解决了一致性问题。(注意是在单线程情况下

另一点为了增加效率cpu在保证as-if-serial语意的情况下会进行指令重排(简单理解:单线程下即在不影响执行最终结果的情况下可以不按照程序的代码顺序执行),编译器也有同样的优化。所以我们在多线程的情况下会遇见各种各样的可见性问题。让我们回到这个问题到底是cpu的问题还是编译器的问题呢。毫无疑问一定是编译器。

再次运行结果:

打印出了数字并且线程结束了,可见子线程确实得到了这个boolean值的变化,那是为什么呢? 这个问题的元凶就是我们的JIT,它把我们的代码大致优化称了这个样子:原因是JIT多次循环读取了flag的值发现没有变化就进行了激进优化结果就变成下面的情况

虽然这个问题并不是由于我们之前说的哪些原因造成可见性问题。但是我们有方式去完全避免可见性问题。

volatile 

我们可以使用volatile关键字去保证可见性,先上代码

结果如下图:

我们成功得到了我们想要的结果,为什么volatile能实现这个功能呢,让我们反编译代码看看加了volatie有什么变化

让我们直接去jvm虚拟机规范里看看这是什么东西?

真相大白 带有这个标示的filed不能被缓存。(即当写一个volatile变量时 JMM会把改线程对应的变量值刷新到主内存中,当读一个volatile变量是JMM会把线程本地的对应变量设置为无效,并重新从主内存读取) 其实现原理是在操作该变量前后加入内存屏障。从刚才的描述我们明白了volatile如何能解决缓存一致性问题。但是volatile如何阻止指令重排呢。原来cpu在读取到内存屏障后会禁止进行指令重排(1.5之前未增强语意只在结尾加入了内存屏障,增强后之前也加入内存屏障严谨不影响结果的指令和该变量重排)

volatile语义只是JMM的一部分,后续我们会介绍JMM的其他语义。以及解释JMM它的定义。今天现总结到这里

总结就到这里欢迎大家指正问题 !!  与君共勉

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值