JVM实验四:内存模型和volatile
1.volatile的出现要解决什么问题?

源代码到最终执行指令过程中,包括了多次的指令重排序
(1)编译器重排序
1 //优化前 2 int x = 1; 3 int y = 2; 4 int a1 = x * 1; 5 int b1 = y * 1; 6 int a2 = x * 2; 7 int b2 = y * 2; 8 int a3 = x * 3; 9 int b3 = y * 3; 10 11 //优化后 12 int x = 1; 13 int y = 2; 14 int a1 = x * 1; 15 int a2 = x * 2; 16 int a3 = x * 3; 17 int b1 = y * 1; 18 int b2 = y * 2; 19 int b3 = y * 3; 20 21 优化前的代码:交替的读x、y,会导致寄存器频繁的交替存储x和y,最糟的情况下寄存器要存储3次x和3次y; 22 优化后的代码:让x的一系列操作一块做完,y的一块做完,理想情况下寄存器只需要存储1次x和1次y。
(2)处理器重排序:指令级重排序
1 LDR R1, [R0];//操作1 2 ADD R2, R1, R1;//操作2 3 ADD R3, R4, R4;//操作3 4 5 处理器在执行时往往会因为一些限制而等待,如访存的地址不在cache中发生miss,这时就需要到内存甚至外存去取,然而内存和外区的读取速度比CPU执行速度慢得多; 6 对于上面这段汇编代码,操作1如果发生cache miss,则需要等待读取内存外存。看看有没有能优先执行的指令,操作2依赖于操作1,不能被优先执行,操作3不依赖1和2,所以能优先执行操作3,所以实际执行顺序是3>1>2。这里打破了程序执行的有序性。
(3)处理器重排序:内存系统重排序
1 初始化: 2 a = 0; 3 b = 0; 4 5 处理器A执行 6 a = 1; //A1 7 read(b); //A2 8 9 处理器B执行 10 b = 2; //B1 11 read(a); //B2

理论执行顺序:以处理器A为例,a=1 应该先执行,x=b 后执行,根据内存模型的效果来看a=1需要执行A1和A3,x=b则需要执行A2,所以正确的执行顺序应该是A1-A3-A2
实际执行顺序:因为从A处理器的角度看,a=1和x=b的执行顺序先后并无影响,即A2先于A3执行并无问题。因此实际的执行顺序是A1-A2-A3
A1-B2-A3的过程中,由于内存模型中的本地缓存,导致A线程的写操作无法立即被B线程给看到,打破了可见性。
由于处理器有读、写缓存区,写缓存区没有及时刷新到内存,造成其他处理器读到的值不是最新的,使得处理器执行的读写操作与内存上反应出的顺序不一致。
(4)重排序的影响
不论哪种重排序都可能造成共享变量中线程间不可见,这会改变程序运行结果。所以需要禁止对那些要求可见的共享变量重排序。
2.volatile如何解决重排序问题
(1)阻止编译重排序
禁止编译器在某些时候重排序
(2)阻止指令重排序和内存系统重排序(使用内存屏障或Lock前缀指令)
- 在每个volatile写操作的前面插入一个StoreStore屏障;
- 在每个volatile写操作的后面插入一个StoreLoad屏障;
- 在每个volatile读操作的后面插入一个LoadLoad屏障;
- 在每个volatile读操作的后面插入一个LoadStore屏障。
- 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保 volatile写之前的操作不会被编译器重排序到volatile写之后。
- 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保 volatile读之后的操作不会被编译器重排序到volatile读之前。
- 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。
在volatile中,所插入的内存屏障将不允许 volatile 字段写操作之前的内存访问被重排序至其之后;也将不允许 volatile 字段读操作之后的内存访问被重排序至其之前。
3.volatile为何无法保证原子性
volatile保证了有序性和可见性。在未添加volatile之前,写操作包含了写缓存-写内存这两步。在添加了volatile之后,写操作是一个单独的操作,不可分割。所以记住一点,volatile保证的是多线程下面读写操作这个粒度的原子性,例如下图中添加了锁的set和get方法。

但volatile无法保证多线程下粒度更大的原子操作,例如i++
i++包括了读内存、自加、写内存三个步骤,但是这三个操作的原子性无法保证
4.实验---指令重排序

1 public class Barrier { 2 int a = 0; 3 int b = 0; 4 int x = 0; 5 int y = 0; 6 private static ExecutorService executorService=Executors.newSingleThreadExecutor(); 7 private static ExecutorService executorService1=Executors.newSingleThreadExecutor(); 8 private static ExecutorService executorService2=Executors.newSingleThreadExecutor(); 9 10 public static void main(String ... args) throws InterruptedException { 11 for (int i=0;i< 1000000;i++){ 12 //初始化: 13 Barrier barrier=new Barrier(); 14 //处理器A执行 15 executorService1.submit(()->{ 16 barrier.a = 1; //A1 17 barrier.x = barrier.b; //A2 18 print(barrier); 19 }); 20 21 //处理器B执行 22 executorService2.submit(()->{ 23 barrier.b = 2; //B1 24 barrier.y = barrier.a; //B2 25 print(barrier); 26 27 }); 28 } 29 30 } 31 public static void print(Barrier barrier){ 32 executorService.submit(()->{ 33 if(barrier.x==0&&barrier.y==0){ 34 System.out.println(String.format("=======>%s,%s,%s,%s",barrier.a,barrier.b,barrier.x,barrier.y)); 35 }else { 36 System.out.println(String.format("%s,%s,%s,%s",barrier.a,barrier.b,barrier.x,barrier.y)); 37 } 38 }); 39 } 40 }


浙公网安备 33010602011771号