原理

编译时,在对volatile变量的赋值操作指令后加个加锁的空操作了强制把修改立即写回主内存,同时其他工作内存往执行引擎中传递被volatile修饰的变量的值时,必须从主内存中获取然后立刻传给工作引擎,这样保证读取到的是最新值,即为内存可见性,另外修改同步回内存,意味着所有之前的操作都已经执行完成,这样便形成了“指令重排序无法越过内存屏障”的效果了。

作用

  • 内存可见性
    • volatile变量对所有线程是立即可见的,对volatile变量所有的写操作都能立刻反应到其他线程之中,只保证了可见性
    • 但同一时间都对该值做了修改的话还是会冲突,也就是说其他线程的修改还来不及在你的操作执行之前通知到你的话,那么修改就会冲突
    • 因此只适用于:
      1. 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
      2. 变量不需要与其他的状态变量共同参与不变约束。
      3. 即所有操作的顺序可以打乱,结果仍一样。
    • 因此,我觉得volatile修饰的变量只保证读一致性,每一个读操作在读时所读到的值确保是最新值,但写就不一定了,如果同时修改且写回主内存时来不及通知其他线程而其他线程的修改也到了主内存,这时便产生了冲突。但volatile的锁是最轻量的,它只在编译时,在对volatile修饰的变量赋值的指令后加个加锁的空操作而不是对该值加锁,那么该变量可以任意读取,且读取到的数据都为最新值,只需忍受同时修改时可能会发生的冲突,有些情况下很值得。
    • 例如如果两条线程要做的操作同样是把原值加上某值,然后恰好同时执行,因为加锁的空操作都立马写回主内存,这是就会产生写冲突,但如果是其中一个线程先执行该指令,之后会立马写回主内存,然后另一条线程执行到该语句,然后去主内存中取值立马交给执行引擎执行,这是就是最新的值却不会产生写冲突。而如果两个线程要做的操作都是把false设置成true,然后因为可见性,其他等待在该状态上的线程会相当于立马得到通知。所以如果只需要读一致性,可以忍受可能发生的写冲突的话,volatile可以提供比同步关键字更好的性能,因为这个可以随便读且读到的值是最新的值。
  • 禁止指令重排序
    • 指令重排序是编译优化的技术一种,例如在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待,但它只保证重排序后线程内表现为串行的语义,即重排序后如果是单线程执行,那么结果一定跟排序前一样。所以重排序后,并行时会发生很多我们之前无法想象的问题。
    • 指令重排序的规则之一是有依赖的语句之间的相对顺序必须得到保证,否则语义会出现错误,然后其他的语句可以自由插入它们之间。加锁的空操作使得修改立即写回主内存,这使得之前所做的操作都已经执行完成,为了达到storeload屏障,后面的指令无法重排序到该加锁空操作之前,因此该加锁空操作就像个内存屏障。
posted on 2017-11-15 20:54  一个人的合唱  阅读(130)  评论(0编辑  收藏  举报