Java硬件基础(三)-如何解决并发问题及volatile、synchronized等关键字实现

    承接上文《处理器针对消息交互的性能优化及带来的并发问题》可知,处理器针对性能问题提供了写缓冲器、无效化队列以及存储转发等优化技术,但是这一系列技术引发了并发问题,如何解决这些问题是本文所讲述的重点。

一、背景

    写缓冲器、无效化队列、存储转发等优化技术可能引发重排序问题(重排序主要包括指令重排序、存储子系统重排序,本文主要针对存储系统重排序进行讲解),进而导致可见性、有序性、原子性得不到保障,因此需要采取某种措施防止这些问题的出现。

二、内存屏障

    内存屏障是被插入两个CPU指令之间的一种指令,用来禁止处理器指令发生重排序(像屏障一样),从而保障有序性的。另外,为了达到屏障的效果,它也会使处理器写入、读取值之前,将写缓冲器的值写入高速缓存,清空无效队列,实现可见性。

    举例:将写缓冲器数据写入高速缓存,能够避免不同处理器之间不能访问写缓冲器而导致的可见性问题,以及有效地避免了存储转发问题;清空无效队列保证该处理器上高速缓存中不存在旧的副本,进而拿到最新数据

    基本内存屏障:

    LoadLoad屏障: 对于这样的语句 Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 
    StoreStore屏障:对于这样的语句 Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 
    LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被执行前,保证Load1要读取的数据被读取完毕。 
    StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的(冲刷写缓冲器,清空无效化队列)。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

    以上的四种屏障主要依据不同处理器支持的重排序(读写,读读,写写,写读)来确定的,比如某些处理器只支持写读重排序,因此只需要StoreLoad屏障

    下面对上述的基本屏障进行利用,以针对不同的目的用相应的屏障。(个人理解,如有错误大家可以指出来),下面主要针对volatile关键字进行叙述。

三、volatile可见性保障

    主要分为加载屏障(Load Barrier)和存储屏障(Store Barrier)

    加载屏障:StoreLoad屏障作为万能屏障,作用是冲刷写缓冲器,清空无效化队列,这样处理器在读取共享变量时,因为本高速缓存中的数据是无效的,因此先从主内存或其他处理器的高速缓存中读取相应变量,更新到自己的缓存中 

    存储屏障:同样使用StoreLoad屏障,作用是将写缓冲器内容写入高速缓存中,使处理器对共享变量的更新写入高速缓存或者主内存中 ,同时解决存储转发问题,使得写缓冲器中的数据不存在旧值

    以上两种屏障解决可见性问题

四、volatile有序性保障

    主要分为获取屏障(Acquire Barrier)和释放屏障(Release Barrier)

    获取屏障:相当于LoadLoad屏障和LoadStore屏障的组合,它能禁止该屏障之前的任何读操作与该屏障之后的任何读、写操作之间进行重排序; 
    释放屏障:相当于LoadStore屏障与StoreStore屏障的组合,它能够禁止该屏障之前的任何读、写操作与该屏障之后的任何写操作之间进行重排序

   这两个屏障我个人理解不同去记住他们,对于其实大家记住volatile修饰的字段和普通修饰的字段同样不可以重排序,因此只要存在读写、写写、写读、读读等操作,包含了volatile关键字,都会在操作指令之间插入屏障的,具体插入什么屏障可以根据对应的操作插入。

    有序性相应案例:

                            

    如图所示,针对写线程,为了保证前两步优先于第三步提交,则插入了释放屏障,禁止该屏障之前的任何读、写操作与该屏障之后的任何写操作之间进行重排序。(在这边我理解,如果第三步操作之后还有操作,那么还会插入屏障)

   针对读线程,必须让V的读操作优先于下面的操作,在这里引出一个新的概念(猜测执行,就是说处理器针对if语句,会先操作内部,然后等到条件满足,这样能节约时间,这时候就是一种重排序问题了,大家可以好好想想)

五、synchronized关键字实现

    synchronized关键字对于可见性、有序性实现方式与volatile关键字类似,synchronized字节码指令大家可以看到总是有monitorenter与monitorexit指令配对产生,那么synchronized会在monitorenter之前插入获取屏障、monitorexit之后插入释放屏障保证有序性,依然通过StoreLoad屏障实现可见行,对于原子性则通过锁来实现,锁这一块是作者下面将要详解的内容。

 

结束语:

    本文主要介绍如何通过内存屏障来解决这些问题以及一些关键字实现的原理。

    谢谢大家阅读,请大家多多指教文章中的不足,互相学习!!

 

 

 

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值