1. 被 volatile 变量修饰后特点有序性 和 可见性volatile 的内存语义写一个 volatile 变量时 JMM 会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。当读一个 volatile 变量时JMM 会把该线程对应的本地内存设置为无效直接从主内存中读取共享变量。所以 volatile 的写内存语义是直接刷新到主内存中读的内存语义是直接从主内存中读取。2. 内存屏障2.1 是什么内存屏障也称内存栅栏内存栅障屏障指令等是一类同步屏障指令是 CPU 或编译器在对内存随机访问的操作中的一个同步点使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作避免代码重排序。内存屏障其实就是一种 JVM 指令Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令通过这些内存屏障指令volatile 实现了 Java内存模型中的可见性和有序性但volatile 无法保证原子性。内存屏障之前的所有写操作都要回写到主内存内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)。因此重排序时不允许把内存屏障之后的指令重排序到内存屏障之前。一句话对一个 volatile 域的写, happens-before 于任意后续对这个 volatile 域的读也叫写后读。2.2 volatile 凭什么可以保证可见性和有序性直接读写主内存 内存屏障强制刷新内存屏障禁止指令重排序2.3 JVM 提供了四种内存屏障指令在每一个 volatile 写操作前面插入一个 StoreStore 内存屏障在每一个 volatile 写操作后面插入一个 StoreLoad 内存屏障在每一个 volatile 读操作前面插入一个 LoadLoad 内存屏障在每一个 volatile 读操作后面插入一个 LoadStore 内存屏障3. volatile 特性3.1 可见性保证不同线程对这个变量进行操作时的可见性即变量一旦改变所有线程立即可见。代码演示publicclassVolatileSeeDemo{staticbooleanflagtrue;//不加volatile没有可见性//static volatile boolean flag true; //加了volatile保证可见性publicstaticvoidmain(String[]args){newThread(()-{System.out.println(Thread.currentThread().getName()\t come in);while(flag){}System.out.println(Thread.currentThread().getName()\t flag被修改为false,退出.....);},t1).start();//暂停2秒钟后让main线程修改flag值try{TimeUnit.SECONDS.sleep(2);}catch(InterruptedExceptione){e.printStackTrace();}flagfalse;System.out.println(main线程修改完成);}}解决使用 volatile 修饰共享变量就可以达到上面的效果被 volatile 修改的变量有以下特点线程中读取的时候每次读取都会去主内存中读取共享变量最新的值然后将其复制到工作内存线程中修改了工作内存中变量的副本修改之后会立即刷新到主内存3.2 有序性volatile 关键字通过内存屏障Memory Barrier和happens-before规则来保证有序性具体实现如下编译器和处理器会对指令进行重排序以优化性能但在多线程环境下可能导致不可预期的结果。volatile 变量的读写操作会插入内存屏障禁止特定类型的重排序写操作Store在 volatile 写操作前插入 StoreStore 屏障确保所有普通写操作非 volatile在此之前完成。在 volatile 写操作后插入 StoreLoad 屏障防止 volatile 写与后续的 volatile 读/写重排序。读操作Load在 volatile 读操作后插入 LoadLoad 屏障和 LoadStore 屏障确保后续操作不会重排到 volatile 读之前。happen-before 规则根据 JMMJava 内存模型的 happens-before 原则- 写操作先行于读操作对 volatile 变量的写操作 happens-before 后续所有对该变量的读操作。 - 编译器/处理器必须遵守此规则不会破坏这种顺序性。volatile 通过内存屏障限制指令重排序结合happens-before 规则和强制主内存访问确保多线程环境下对变量的操作按预期顺序执行从而保证有序性。3.3 不保证原子性奥volatile 变量的复合操作如 i 不具有原子性。在 JVM 字节码 i 操作分为三步数据加载 数据计算 数据赋值间隙期不同步非原子操作。4. 如何正确使用 volatile单一赋值可以 复合赋值不可以状态标志判断业务是否结束开销较低的读 写策略双端锁5. 总结5.1 内存屏障是什么是 一 种 屏 障 指 令它使得 CPU 或 编译器 对屏障指令的前和后 所发出的内存操作 执行一个排序的约束。5.2 内存屏障能干嘛5.3 内存屏障四大指令在每一个 volatile 写操作前面插入一个 StoreStore 内存屏障在每一个 volatile 写操作后面插入一个 StoreLoad 内存屏障在每一个 volatile 读操作前面插入一个 LoadLoad 内存屏障在每一个 volatile 读操作后面插入一个 LoadStore 内存屏障