volatile重排范围是同一个方法中禁止重排还是不同方法也行?
加入有4个共享变量a,b,x,y。设定b为volatile,按理说b读会为
loadload 读b loadstore,我的理解是b之前读不可以和b重排,b之后写不可以和b交换,那么
写a loadload volatile读b loadstore 读a 会不会重排为这样 loadload 读a 写a volatile读b loadstore
同理,写会不会
读a storestore volatile写b storeload 写a 会不会重排为这样 storestore 写a 读a volatile写b storeload
接下来测试:
线程一:通过方法一去 读a 写b
线程二:通过方法二去 写a
预期:读a-> volatile写b->写a
if(a == 2){
System.out.println("volatile error");
}
storestore
b = 1;
storeload
a = 2;
结果:storestore 写a->读a-> volatile写b storeload
storestore
a = 2;
if(a == 2){
System.out.println("volatile error");
}
b = 1;
storeload
分析:
one.start();
two.start();
可能volatile变量并不能禁止不同方法之间的重排,所以线程二可以先执行
ps:有人可能会说一般这应该是a做volatile,但这是可见性(a变量是否可见)了,并不是重排序特性,个人认为。
package VolatileTest;
import java.util.HashSet;
import java.util.Set;
public class VolatileTest {
static int a = 0;
volatile static int b = 0;
static int x = 0;
static int y = 0;
public void thread1Run(){
if(a == 2){
System.out.println("volatile error");
}
// x = a;
b = 1;
}
public void thread2Run(){
//y = b;
a = 2;
}
public static void main(String[] args) throws InterruptedException {
Set<String> resultSet = new HashSet<>();
Thread one = new Thread(()->{
VolatileTest v1 = new VolatileTest();
v1.thread1Run();
});
Thread two = new Thread(()->{
VolatileTest v2 = new VolatileTest();
v2.thread2Run();
});
one.start();
two.start();
Thread.sleep(100);
//System.out.println("x = " + x +" y = " + y);
}
}
其中一种结果
volatile error
例子二
还是2个不同方法
线程一:通过write去 volatile写flag,写number
线程二:通过read去 volatile读a
理想中:volatile写flag ->写number ->volatile读a
storestore
this.flag = true;
storeload
this.number = 20;
loadload
while (this.flag){
this.result = this.number + 1;
}
loadstore
结果:volatile读a->volatile写flag ->写number
while (this.flag){
this.result = this.number + 1;
}
this.flag = true;
this.number = 20;
分析:
volatile确实不能禁止2个方法之间的重排序
package VolatileTest;
//指令重排测试
public class VolatileTest2 {
static Integer number = 10;
static volatile boolean flag = false;
static Integer result = 1;
public void write(){
this.flag = true; // L1
this.number = 20; // L2
}
public void reader(){
while (this.flag){ // L3
this.result = this.number + 1; // L4
}
}
public static void main(String[] args) {
Thread one = new Thread(()->{
VolatileTest2 v1 = new VolatileTest2();
v1.write();
});
Thread two = new Thread(()->{
VolatileTest2 v2 = new VolatileTest2();
v2.reader();
});
one.start();
two.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(result);
}
}
其中一种结果:1
说明volatile并不能保证2个methed之间的重排序。
那到底volatile重排特性体现在哪里?
只能在同一方法中重排(这个好像没用,因为已经有了as-if-serial 和 happen-before,确保了同一方法中程序执行结果重排序后不变)或者我的测试用例错了
那volatile重排序表是什么意思呢?
其实我感觉volatile重排规则没怎么用得到,都是as-if-serial 和 happen-before用的上。
感觉上volatile是对定义变量禁止重排
new的时候。百度上我也只能透过这个例子稍微看出一点volatile重排特性。