JUC学习:volatile与内存屏障

1 volatile的基本信息

1.1 volatile的特性

        可见性、有序性(禁止重排 )。

        volatile不具备原子性。在一个线程(t1)操作volatile变量时,另一个线程(t2)更新了该volatile变量的值,则t1线程需要去主内存中获取最新的volatile变量值,t1线程之前的操作则全部作废,出现操作丢失问题。由此可见volatile解决的是可见性的问题,而无法保证其原子性。所以在多线程修改主内存变量的情况下必须加锁。

1.2 volatile变量的读写过程

  • 在对一个volatile变量进行写操作时,JMM会将该线程工作内存中的该volatile变量立即刷新回主内存中,并通知其它线程该变量已更新。
  • 在对一个volatile变量进行读操作时,JMM会将该线程工作内存中的该volatile变量置为无效,重新回到主内存中读取该变量。

1.2.1 8个原子性操作

  • read(读取):作用于主内存,将变量从主内存读取到工作内存中。
  • load(加载):作用于工作内存,将读取到工作内存中的变量放入工作内存的变量副本中(工作线程修改的都是自己副本中的变量)。
  • use(使用):作用于工作内存,将工作内存中的变量副本中的值传递给执行引擎进行操作。
  • assign(赋值):作用于工作内存,将执行引擎中返回的值赋值给工作内存中的变量副本。
  • store(存储):作用于工作内存,将赋值完毕的变量副本写回给主内存。
  • write(写入):作用于主内存,将工作内存写回的值赋值给主内存中的变量。
  • lock(锁定):作用于主内存,在工作内存写回变量的过程中,为防止多个线程同时向主内存写入,因此需要加锁,确保同一时刻只能有一个线程写入。
  • unlock(解锁):作用于主内存,对应lock,线程写入后解锁,其他线程才能继续写入该变量。

1.2.2 volatile变量的读写过程流程图

        从volatile的读写过程中,可以看出改变volatile变量后通知其它线程实现了volatile的可见性。

1.3 volatile的适用场景

  • 状态标志:将volatile修饰的状态变量改变时,会立刻通知其他线程。
  • 开销较低的读,写锁策略:当读远多于写的情况下,可以将变量设置为volatile,则只需要在写操作上添加sync同步即可,每次写操作都会通知读操作去读取最新的值。

  • 单例模式中为变量添加volatile,保证多线程情况下new对象时禁止指令重排的问题(先获取一块内存地址,再通过这块内存初始化对象,再返回对象的地址);

2 内存屏障

2.1 内存屏障的作用

        内存屏障是为了保证volatile的有序性,即禁止指令重排序(在多线程环境下,指令会根据编译器等重排序)的一种屏障指令。主要用于禁止屏障两侧的指令重排。

2.2 内存屏障的实现方式

        当一个变量被定义为volatile时,在其字节码层面会为这个字段的flags里添加一个ACC_VOLATILE,jvm在生成对应的字节码指令时,发现操作的是volatile变量时,会在相应位置插入内存屏障。

2.3 内存屏障分类

        通过读屏障与写屏障之间的配合,能够实现指令的顺序执行,从而达到有序性的特点。

2.3.1 写屏障

        告诉处理器,在写屏障之前的所有存储在工作内存中的变量数据同步到主内存中。即在写屏障之前必须将所有写入指令执行完成才能继续执行。

       在写指令之前插入写屏障,将写屏障之前的缓存数据强制刷回主内存中。

2.3.2 读屏障

        处理器在读屏障之后的操作只能在读屏障之后进行。即保证读屏障之后的读操作一定能读取到最新的数据。

        在读指令之前插入读屏障,让工作内存或cpu中的缓存数据失效,从而回到主内存中读取最新的数据。

2.3.3 四种屏障

  • LoadLoad屏障(Load1;LoadLoad;Load2):保证Load1的读取在Load2之前执行。
  • StoreStore屏障(Store1;StoreStore;Store2):保证在Store2写操作之前,Store1的写操作已经刷新到主内存。
  • LoadStore屏障(Load1;LoadStore;Store1):保证在Store1写操作之前,Load1的读操作已经完成。
  • StoreLoad屏障(Store1;StoreLoad;Load1):保证在Load1读操作之前,Store1以及之前的写操作已经刷新回主内存中。

2.4 volatile规则之四种屏障的位置

  • 当第一个操作为volatile读操作时,无论第二个是什么操作,都不能重排;
  • 当第二个操作为volatile写操作时,无论第一个操作是什么,都不能重排;
  • 当第一个操作为volatile写操作,第二个为volatile读操作时,不能重排;

2.4.1读操作内存屏障指令示意图

 2.4.2 写操作内存屏障指令示意图

 3 总结

        通过上述内存大致可以看出,volatile的读写流程实现了其可见性,即读写完立即通知其他线程获取最新的值。volatile的内存屏障则实现了其有序性(禁止重排),通过四种读写屏障强制指定读写命令的顺序。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值