目录
1.保证内存可见性/指令重排序
2.不保证原子性
一.保证内存可见性/指令重排序
import java.util.Scanner;
public class demo16 {
static class Counter{
public static int flag = 0;
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(()->{
while(counter.flag == 0){
//执行循环,但是此处什么都不做;
}
System.out.println("t1结束");
});
t1.start();
Thread t2 = new Thread(()->{
//让用户输入一个数字,赋值给flag;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个数字:");
Counter.flag = scanner.nextInt();
});
t2.start();
}
}
由上述程序:预期当用户给 t2线程输入一个非零数字之后,t1线程中counter.flag不满足等于0这个条件将会结束循环,随之线程结束。
但由运行结果可知:t1线程仍在循环,并没有结束。
这是由于,t1的工作是:1.LOAD读内存中的数据到CPU寄存器中,2.TEST检测到CPU寄存器的值是否和预期一样。但是由于读内存比读CPU寄存器慢几千倍,几万倍,即当前的 t1的操作主要慢在LOAD上,编译器发现每次LOAD的结果都一样,就直接进行优化,相当于只从内存中读取一次,然后后续直接从寄存器里进行反复TEST。则会导致一种特殊情况,编译器看到这个线程没做修改,所以就进行了优化,但是其他的线程对这个变量修改了!
为了预防编译器误判,提供了一个干预优化的途径,即使用volatile关键字,来保证内存可见性。
volatile操作相当于显式的禁止了编译器进行优化,是给这个对应这个变量加上了“内存屏障”(特殊的二进制指令),JVM因为有这个内存屏障的存在,每次都会重新读取整个内存的结果,而不是草率的进行优化。
import java.util.Scanner;
public class demo16 {
static class Counter{
volatile public static int flag = 0;
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(()->{
while(counter.flag == 0){
//执行循环,但是此处什么都不做;
}
System.out.println("t1结束");
});
t1.start();
Thread t2 = new Thread(()->{
//让用户输入一个数字,赋值给flag;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个数字:");
Counter.flag = scanner.nextInt();
});
t2.start();
}
}
二.不保证原子性
volatile不保证原子性,保证原子性的是synchronized。