Java并发编程可见性问题

1 计算机硬件中可见性问题

1.1 可见性问题的来源

单核CPU如果想要再通过提升频率会带来诸多问题凸显,如散热问题,能耗问题,技术难度很高,性能提升遇到瓶颈。改为采用多核cpu架构。

cpu运算速度比主存的存取速度快很多,为了提高处理速度,给每一个核增加高速缓存,cpu从高速缓存读取数据比直接从主存中读取数据快的多。通常多核架构的每个cpu都有他们自己独有的高速缓存:一级高速缓存L1(L1P+L1D),二级高速缓存L2。在同一个cpu芯片上的所有cpu还同时共享一个三级高速缓存L3。这样做的优点是:
1.写缓存区可以保证指令流水线持续运行,可以避免由于cpu停顿下来等待向内存写入数据而产生的延迟。
2.通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总线的占用。

但是,随着多核cpu的使用也出现了一些需要解决的问题,如何保证主存中共享数据的并发问题。这里就出现了并发编程中的三大问题:原子性问题、可见性问题、有序性问题。这篇笔记主要理解一下可见性问题。

可见性:一个线程对共享变量的修改,另一个线程能够立刻可见,我们称该共享变量具备内存可见性。

可见性问题的产生:由于每个线程可能会运行在不同cpu内核中,因此每个线程拥有自己的高速缓存。同一份数据可能会被缓存到多个cpu内核中,在不同cpu内核中运行的线程看到同一个变量的缓存值就会不一样,就可能发生内存的可见性问题。

1.2 可见性问题的解决

于是为了保证每个cpu core访问数据的一致性,需要各个cpu在访问高速缓存时遵循一套缓存一致性协议。MESI(Modified、Exclusive、Shared、Invalid)就是一种最常用的缓存一致性协议。共享变量有上述四种状态,共享变量在每种状态下都可能遇到4种操作(本地读、本地写、远程读、远程写)。在每种状态下的操作都遵循MESI协议的规则就能够保证共享变量的可见性。其状态转换图如下:
在这里插入图片描述
状态转换规则如下:
在这里插入图片描述

2 Java中的可见性问题

在Java中,Java程序是运行在JVM(Java Virtual Machine)中的。JVM中运行Java程序时,在使用内存遵循一套规则,我们叫做JMM(Java Memory Mode)。这一套规则用于解决可见性和有序性问题。
JMM规定给每一个Java线程一个自己私有的工作内存(类比cpu高速缓存),当使用主存(类比cpu的内存)中的变量时,将主存中变量复制到工作内存中。所以这套内存模型与cpu高速缓存模型其实有相似之处,这里就不多讲JMM了。在JVM中和Java语言中包含了JMM的规则指令,将这些指令体现到Java的关键字语义中。
所以在执行Java程序时,只有当共享变量被volatile关键字修饰了,该变量所在的缓存才被要求进行缓存一致性的校验。volatile关键字修饰共享变量后,会在操作该共享变量时添加lock前缀指令,该指令有以下三种功能:

  1. 将当前cpu缓存行的数据立即写回系统内存;
  2. lock前缀指令会引起在其他cpu中缓存了该内存地址的数据无效;
  3. lock前缀指令禁止指令重排序;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值