java并发编程-volatile内存语义

java并发编程-volatile内存语义

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:volatile有以下两个作用:

  1. 保证被volatile修饰的共享变量对所有线程总数可见的,也就是当一个线程修改了一个被volatile修饰共享变量的值,新值总是可以被其他线程立即得知。
  2. 禁止指令重排序优化

提示:以下是本篇文章正文内容,下面案例可供参考

一、volatile的可见性

关于volatile的可见性作用,我们必须意识到被volatile修饰的变量对所有线程总数立即可见的,对volatile变量的所有写操作总是能立刻反应到其他线程中。

1 public class VolatileVisibilitySample {
2 volatile boolean initFlag = false;
3 public void save(){
4 this.initFlag = true;
5 String threadname = Thread.currentThread().getName();
6 System.out.println("线程:"+threadname+":修改共享变量initF
lag");
7 }
8 public void load(){
9 String threadname = Thread.currentThread().getName();
10 while (!initFlag){
11 //线程在此处空跑,等待initFlag状态改变
12 }
13 System.out.println("线程:"+threadname+"当前线程嗅探到ini
tFlag的状态的改变");
14 }
15 public static void main(String[] args){
16 VolatileVisibilitySample sample = new VolatileVisibilit
ySample();
17 Thread threadA = new Thread(()>{
18 sample.save();
19 },"threadA");
20 Thread threadB = new Thread(()>{
21 sample.load();
22 },"threadB");
23 threadB.start();
24 try {
25 Thread.sleep(1000);
26 } catch (InterruptedException e) {
27 e.printStackTrace();
28 }
29 threadA.start();
30 }
31 }

二、volatile禁止重排优化

volitile关键字的另一个作用就是禁止指令重排。如何禁止?内存屏障

2.1硬件层的内存屏障

Intel硬件提供了一系列的内存屏障,主要有:

  1. lfence,是一种Load Barrier 读屏障
  2. sfence, 是一种Store Barrier 写屏障
  3. mfence, 是一种全能型的屏障,具备ifence和sfence的能力
  4. Lock前缀,Lock不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU总线和高速缓存加锁。

2.2JVM提供四种内存屏障

在这里插入图片描述
所谓内存屏障就是在八大原子操作之间添加了内存屏障,使他之间按照规则禁止重排。volatile变量正是通过内存屏障实现其在内存中的语义。

双重检测锁
1 public class DoubleCheckLock {
2 private static DoubleCheckLock instance;
3 private DoubleCheckLock(){}
4 public static DoubleCheckLock getInstance(){
5 //第一次检测
6 if (instance==null){
7 //同步
8 synchronized (DoubleCheckLock.class){
9 if (instance == null){
10 //多线程环境下可能会出现问题的地方
11 instance = new DoubleCheckLock();
}}}
15 return instance;
}}

上述代码一个经典的单例的双重检测的代码,这段代码在单线程环境下并没有什么问题,但如果在多线程环境下就可以出现线程安全问题。原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。
因为instance = new DoubleCheckLock();可以分为以下3步完成(伪代码)

memory = allocate();//1.分配对象内存空间
instance(memory);//2.初始化对象
instance = memory;//3.设置instance指向刚分配的内存地址,此时instance!=null

若发生指令重排

memory=allocate();//1.分配对象内存空间
instance=memory;//3.设置instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成!
instance(memory);//2.初始化对象

使用volatile禁止instance变量被执行指令重排优化即可。

//禁止指令重排优化
private volatile static DoubleCheckLock instance;

总结

提示:想了解更多可以阅读并发编程的艺术这本书

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值