JMM体现在以下几个方面
原子性-保证指令不会受到线程上下文切换的影响
可见性-保证指令不会受cpu缓存的影响
有序性-保证指令不会受cpu指令并行优化的影响
可见性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Slf4j(topic = "JC") public class JC { static boolean run=true; public static void main(String[] args) throws InterruptedException { Thread t=new Thread(()->{ while (run){ } }); t.start();
sleep(1); log.debug("停止t"); run=false; } }
|
初始状态,t线程刚开始从主内存中读取run到自己的工作内存。
JIT编译器将run缓存到工作内存的高速缓存中,减少对主内存中run的访问,提高效率。
解决办法
1
| volatile static boolean run=true;
|
volatile(易变关键字)
它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存。
两阶段终止模式,Balking模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| private Thread monitorThread;
private volatile boolean stop=false;
rivate volatile boolean starting=false;
public void start(){ synchronized (this){ if(starting){ return; } starting=true; } monitorThread=new Thread(()->{ while (true){ if(stop){ log.debug("料理后事"); break; } try { Thread.sleep(1000); log.debug("执行监控记录"); }catch (InterruptedException e){ } } },"monitor"); monitorThread.start(); }
public void stop(){ stop=true; starting=false; monitorThread.interrupt(); }
|
有序性-指令重排序优化
指令重排的前提是,重排指令不能影响结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int num=0; boolean ready=false;
public void actor1(I_Result r){ if(ready){ r.r1=num+num; }else { r.r1=1; } }
public void actor2(I_Result r){ num=2; ready=true; }
|
volatile原理
写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中。
读屏障(lfence)保证在该屏障之后的,对共享变量的读取,加载的是主存中最新数据。