Java指令重排

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Main{
static boolean mark = true;
public static void main(String[] args) throws InterruptedException {
run();
/*加上这句会怎么样?*/
//Thread.sleep(100);
mrak = false;
System.out.println("main end");
}

static void run(){
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (mark){
//这里具体要做什么也会影响到结果
i++;
}
}
}).start();
}
}

1.不加Thread.sleep(100)

程序会正常结束,大多数情况下,在启动新线程的时候,主线程后面的会获得执行。

1
2
3
4
5
6
7
public void run() {
int i = 0;
while (run){
System.out.println("new thread start");
i++;
}
}

通过验证发现, System.out.println(“new thread start”) 并不会输出。

2.加Thread.sleep(100)

奇怪的现象发生了。这时候,新开的线程不会结束。why?
编译的过程有

其中中间代码是和平台无关的,编译器在中间代码后面可以进行一些和平台无关的代码优化,在目标代码生成的时候,编译器当然也可以根据目标平台支持的特性进行优化,常见的指令重排可以是CPU流水线优化,IO重排等。当然JVM也会对你写的代码进行一些优化,发生在编译阶段和运行阶段都有可能。

主要还是编译器以及CPU为了优化代码或者执行的效率而执行的优化操作;应用条件是单线程场景下,对于并发多线程场景下,指令重排会产生不确定的执行效果
(原文链接https://blog.csdn.net/blueheart20/article/details/52117761[链接](https://blog.csdn.net/blueheart20/article/details/52117761))

3 如何防止指令重排?

volatile关键字可以保证变量的可见性,因为对volatile的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache,因为它们都不是全局的,无法保证可见性,可能产生脏读。
volatile还有一个作用就是局部阻止重排序的发生,对volatile变量的操作指令都不会被重排序,因为如果重排序,又可能产生可见性问题。
在保证可见性方面,锁(包括显式锁、对象锁)以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同,例如同步锁保证得到锁时从内存里重新读入数据刷新缓存,释放锁时将数据写回内存以保数据可见,而volatile变量干脆都是读写内存。
(原文链接:https://blog.csdn.net/blueheart20/article/details/52117761[链接](https://blog.csdn.net/blueheart20/article/details/52117761))

1
2
// 在run方法里面进行跨对象访问。也会避免当前这个优化
static volatile boolean run = true;