JAVA并发编程之详解volatile

3 篇文章 0 订阅
2 篇文章 0 订阅

volatile实现原理

例如变量 instance被volatile修饰。然后有如下代码:

instance=new Singleton();

转换为汇编语言后代码如下:

0x01a3d31d: movb $0X0,0X1104800(%esi);
0X01a3de24: lock addl $0X0,(%esp);

有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,通过查IA-32架构软件开发者手册可知,Lock前缀的指令在多核处理器下会引发了两件事。

1)将当前处理器缓存行的数据写回到系统内存。

Lock前缀指令会引起处理器缓存回写到内存。Lock前缀指令导致在执行指令期间,声言处理器的LOCK#信号。在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以独占任何共享内存。但是,在最近的处理器里,LOCK #信号一般不锁总线,而是锁缓存,毕竟锁总线开销的比较大。对于Intel486和Pentium处理器,在锁操作时,总是在总线上声言LOCK#信号。但在P6和目前的处理器中,如果访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反,它会锁定这块内存区域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。

2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

一个处理器的缓存回写到内存会导致其他处理器的缓存无效。IA-32处理器和Intel 64处理器使用MESI(修改、独占、共享、无效)控制协议去维护内部缓存和其他处理器缓存的一致性。在多核处理器系统中进行操作的时候,IA-32和Intel 64处理器能嗅探其他处理器访问系统内存和它们的内部缓存。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。例如,在Pentium和P6 family处理器中,如果通过嗅探一个处理器来检测其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充。

可见性

可见性的意思是,当一个线程修改共享变量时,其他线程能读到修改后的值。

指令重排

程序指令的执行顺序有可能和代码的顺序不一致,这个过程就称之为指令重排。
作用:JVM能根据处理器的特性,充分利用多级缓存,多核等进行适当的指令重排序,使程序在保证业务运行的同时,充分利用CPU的资源,发挥最大的性能!

由于指令重排的特性,为了保证程序在多线程的条件下运行结果能够与单一线程下一致,引入了Happens-Before规则。也就是说,Happens-Before规则主要是用来确保并发情况下数据的正确性。

Happens-Before

如果动作B要看到动作A的执行结果,无论A/B是否在同一个线程中,那么A/B必须满足happens-before规则。

1)如果ActionA和ActionB属于同一个线程,那么就说明ActionA happens-before ActionB。
2)如果ActionA是unlock操作,而ActionB是lock操作,那么ActionA happens-before ActionB。
3)如果A是对volatile变量的写操作,ActionB是对同一个变量的读操作,那么ActionA happens-before ActionB。
4)线程的启动Action happens-before 该线程上的其他动作。
5)线程中任何Action都 happens-before 任何其他线程检测到该线程已经结束、Thread.join调用成功返回,Thread.isAlive返回false。
6)一个线程调用另一个线程的interrupt一定发生在另一个线程中断之前。
7)一个对象的构造函数结束一定发生在兑现finalizer之前。
8)ActionA发生在ActionB之前,ActionB发生在ActionC之前,则ActionA一定发生在ActionC之前。

volatile特性

可见性–对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
原子性–对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。即:使用时必须保证新值不依赖与旧值。

volatile的内存语义

volatile写的内存语义:
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。

volatile读的内存语义:
当读一个volatile变量时,JMM会把改线程对应的本地内存置为无效,从主内存中读取共享变量。

参考书籍:
《Java并发编程的艺术》 --方腾飞

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值