这些重排序可能会导致多线程程序出现内存可见性问题。对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序(并不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理重排序规则会要求Java编译器在生成指令序列时,通过内存屏障(后面会解释)指令来禁止特定类型的处理重排序。
现在的处理器使用写缓冲区临时保存向内存写入的数据。写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总线的占用。虽然写缓冲区有这么多好处,但每个处理器的写缓冲区,仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写操作顺序一致!下面请看下案例:
class Pointer { int a = 0; int b = 0; int x = 0; int y = 0; public void set1() { a = 1; x = b; } public void set2() { b = 1; y = a; } } /** * 重排序测试 */ public class ReorderTest { public static void main(String[] args) throws InterruptedException { int i = 0; while (true) { final Pointer counter = new Pointer(); Thread t1 = new Thread(new Runnable() { @Override public void run() { counter.set1(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { counter.set2(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("i="+(++i)+",x=" + counter.x + ", y=" + counter.y); if (counter.x == 0 && counter.y == 0) { break; } } } }