代码的顺序改变后,虽然变量值没有变化,但是对于线程的使用时容易发生不安全的问题。这里也是我们用Volatile时一再说明不能用于重排的原因。下面我们就为大家分析重排导致线程不安全的实例,以及Volatile为了避免这种不安全的情况的发生,所采取的内存屏障的方法。
1.Volatile重排导致线程不安全
对于有些代码进行重排序之后,虽然对变量的值没有造成影响,但有可能会出现线程安全问题的。具体请看下面的代码
public class NoVisibility{ private static boolean ready; private static int number; private static class Reader extends Thread{ public void run(){ while(!ready){ Thread.yield(); } System.out.println(number); } } public static void main(String[] args){ new Reader().start(); number = 42; ready = true; } }
这段代码最终打印的一定是42吗?如果没有重排序的话,打印的确实会是42,但如果number = 42和ready = true被进行了重排序,颠倒了顺序,那么就有可能打印出0了,而不是42。(因为number的初始值会是0)。
因此,重排序是有可能导致线程安全问题的。
2.Volatile内存屏障防止重排
Volatile实现禁止指令重排优化,从而避免了多线程环境下程序出现乱序执行的现象。
首先了解一个概念,内存屏障(Memory Barrier)又称内存栅栏,是一个CPU指令,它的作用有两个:
(1)保证特定 *** 作的顺序
(2)保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)
由于编译器和处理器都能执行指令重排的优化,如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,也就是说 通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。 内存屏障另外一个作用是刷新出各种CPU的缓存数,因此任何CPU上的线程都能读取到这些数据的最新版本。
在Volatile的写和读的时候,加入屏障,防止出现指令重排,线程安全获得保证。
以上就是java Volatile不能指令重排原因,这也是发挥Volatile内屏障的作用,优势是使用后能够提升整体线程的安全性问题。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)