Java多线程中内存的可见性

Java多线程中内存的可见性

1. 什么是内存的可见性

一个线程对共享变量值的修改能够及时的被其他线程看到。

2. 共享变量在线程间不可见的原因

1、线程的交叉执行
2、重排序结合线程交叉执行
3、共享变量更新后的值没有在工作内存中与主内存间及时更新

重排序:重排序是代码书写的顺序和实际执行的顺序不同。指令重排序是编译器或处理器为了提高程序性能而作的优化

1、编译优化的重排序(对编译器进行的优化)
2、指令集并行重排序(对处理器进行优化)

3、内存系统的重排序(对处理器进行优化)

3. 实现可见性的方式

常用的实现可见性的方式有两种:synchronizedvolatile

synchronized:

首先在JMM中关于Synchronized有两条规定:
1、线程解锁前,必须把共享变量的最新值刷新到主内存中去。
2、线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值

注意:加锁和解锁需要是同一把锁

线程执行互斥代码的过程:

1、获得互斥锁
2、清空工作内存
3、从主内存拷贝变量的最新副本到工作内存
4、执行代码
5、将更改后的共享变量的值刷新到主内存中

上述即为Synchronized实现内存可见性的方式,接下来我们将谈谈voaltile

volatile

volatile关键字
1、能够保证volatile变量的可见性
2、不能保证volatile变量符合澳做的原子性

volatile如何实现内存可见性
深入来说,通过加入内存屏障和禁止重排序优化来实现的:
1、对volatile变量执行写操作时,会在写操作后加入一条store屏障指令(强行刷新主内存中变量的值)
2、对volatile变量执行读操作时,会在读操作前加入一条load屏障指令(读取主内存中变量的值)
通俗的讲,volatile变量在每次被线程访问时,都强迫从主内存中复读该变量的值,而当该变量发生变化时,又会强迫线程将最新值刷新到主内存,这样任何时刻,不同的线程总能看到该变量的最新值

特别注意,volatile不能保证volatile变量操作的原子性,如num++就不是一个原子性的操作,num++这个操作分三步,将内存读到寄存器,在寄存器中自增,以及将变量写回内存

volatile的适用场景
1、对变量的写入操作不依赖其当前值
不满足:number++,count = count*5等(因为这些变量的赋值都依赖上一次的值)
满足:boolean变量、记录温度变化的变量等
2、该变量没有包含在具有其他变量的不变式中:如low < high就是一个不变式

Synchronized和Volatile的比较

  1. volatile不需要加锁,比synchronized更轻量级,不会阻塞线程。
  2. 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于解锁。
  3. synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。

附录

  1. 即使没有保证可见性的措施,很多时候共享变量依然能够在主内存和工作内存见得到及时的更新的原因是:一般只有在短时间内高并发的情况下采会出现变量得不到及时更新的情况,因为CPU在执行时会很快地刷新缓存,所以一般情况下很难看到这种内存不可见的情况。
  2. 对于64位(long、double)变量的读写可能不是原子操作:因为Java内存模型允许JVM将没有volatile修饰的64位数据类型的读写操作划分为两次32位的读写操作来执行。
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值