Java:能不能实现一个由指令重排引起的多线程不安全例子?
听说当两条指令互相不依赖的时候,在cpu或者jvm那儿可能会为了提高性能而进行指令重排。
数据依赖
比如下面两条代码就没有数据依赖:
int a = 5;
int b= 2;
这两条指令先后顺序并不影响程序运行逻辑,理论来说重排是没有问题的。
例子
但是,在多线程的时候就会出现问题,单个线程有依赖可以检测出来,
多个线程操作同一个对象的时候,因为每个线程都有自己的工作区,主存与工作内存交互不及时,就会导致数据出问题。
代码:
代码分析:
/** * 理论上来说: * 1.线程1先执行完,线程2开始执行 * a=1; * flag=true; * if (flag) { * a = a + 5; * System.out.println("reValue:" + a); * } * 运行结果是:输出reValue:6 * * 2.线程1先执行一半,线程2执行 * a=1; * if (flag) { * a = a + 5; * System.out.println("reValue:" + a); * } * flag=true; * 运行结果:无。 * * 3.线程2先执行,线程1开始执行 * if (flag) { * a = a + 5; * System.out.println("reValue:" + a); * } * a=1; * flag=true; * 运行结果是:无。 * * 如果在线程1中发生指令重排!!!!! * 就会出现: * 线程1先执行一半,线程2执行 * flag=true; * if (flag) { * a = a + 5; * System.out.println("reValue:" + a); * } * a=1; * 运行结果:控制台输出reValue:a */
运行结果:
我i5第十二代处理器,运行了一晚上,只出了这一次指令重排。它真的,我哭死。
还好运行出来一次,我勉强信了。
原因
它如何产生:就是因为每个线程都有自己的工作内存,而工作内存与主内存交互不够及时。
解决方案:
当你觉得某个变量会因为指令重排而导致线程不安全,那么对其使用volatile的声明,它使得变量及时地在主存中更新 :
1、对volatile变量的写会立即刷新到主存
2、对volatile变量的读会读主存中的新值