并发编程之内存可见性

一、如何保证共享变量的可见性

  • volatile
    • 内存屏障 StoreStore。
    1. volatile执行写操作时,会在写操作后加入一条store屏障指令
    2. volatile执行读操作时,会在读操作后加入一条load屏障指令
    • 禁止重排序

写volatile变量的过程:

  1. 改变线程工作内存中volatile变量副本的值
  2. 将改变后的副本的值从工作内存刷新到主内存

读volatile变量的过程:
3. 从主内存中读取volatile变量的最新值到线程的工作内存
4. 从工作内存中读取volatile变量的副本

  • synchronized
    • 在线程解锁前将工作内存中的值刷新至主内存
    • 在线程加锁前清空工作内存,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁需要的是同一把锁
    1. 获得互斥锁
    2. 清空工作内存
    3. 从主内存中拷贝共享变量的副本到工作内存
    4. 执行代码
    5. 将共享变量刷新回主内存
    6. 释放互斥锁

二、从number++看原子操作

number++不是原子操作,可细分为三步:

  1. 读取number的值
  2. 将number的值+1
  3. 写入最新的number的值
synchronized(this){
	number++;
}
Lock lock = new Reetrantlock();
lock.lock();
	try
	number++;

加入synchronized,将对number变量进行加锁,在当前线程执行完之前,其他线程无法对number进行操作。变为原子操作。
变量为volatile变量,无法保证原子性

三、volatile适用场合

  • 对变量的写入操作不依赖其当前值
  1. 不满足:number++、count= count+5等
  2. 满足:boolean变量、记录温度变化的变量等
  • 该变量没有包含在具有其他变量的不变式中
  1. low<up

四、重排序

  1. 编译器优化的重排序(编译器重排序)
  2. 指令并行重排序(处理器重排序)
  3. 内存系统的重排序(处理器重排序)

线程之间的交叉执行会破坏原子性

五、as-if-serial原则

无论如何进行重排序,程序执行的结果都应该与代码顺序执行的结果一致。(Java编译器、运行时和处理器都会保证Java在单线程下遵循as-if-serial语义

六、volatiele 和synchronized比较

  • volatile不需要加锁,比synchronized更加轻量级,不会阻塞线程。
  • 从内存可见性来讲。volatile读相当于加锁,volatile写相当于解锁
  • synchronized既能保证可见性,又能够保证原子性。volatile仅能保证可见性,但无法保证原子性
  • final也可以保证内存的可见性(一旦赋值就无法更改)

七、64位变量(long,double)

对64位变量(long或者double)的读写可能不是原子操作
java内存模型允许jvm将没有被volatile修饰的64位数据类型的读写操作划分为两次对32位的读写操作来进行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值