final class SingletonLazyVolatile {private SingletonLazyVolatile() { }// 问题1:为什么加入 volatile 关键字?// 答:防止指令重排序 造成返回对象不完整 。如 TODOprivate static volatile SingletonLazyVolatile INSTANCE = null;// 问题2:对比实现3(给静态代码块加synchronized) 说出这样做的意义?// 答:没有锁进行判断、效率较高public static SingletonLazyVolatile getInstance() {if (INSTANCE != null) {return INSTANCE;}// 问题3:为什么要在这里加空判断,之前不是判断过了吗?// 答:假入t1 先进入判断空成立,先拿到锁,然后到实例化对象这一步(未执行)//同时 线程 t2 获取锁进入阻塞状态,若 t1 完成创建对象后,t2 没有在同步块这进行判空,t2 会再新创建一个对象,//导致 t1 的对象被覆盖 造成线程不安全 。synchronized (SingletonLazyVolatile.class) {// t1if (INSTANCE != null) {return INSTANCE;}INSTANCE = new SingletonLazyVolatile();// t1这行代码会发生指令重排序,需要加入 volatile// 如:先赋值指令INSTANCE = new SingletonLazyVolatile,导致实例不为空,下一个线程会判空失败直接返回该对象// 但是构造方法()指令还没执行,返回的就是一个不完整的对象 。return INSTANCE;}}}
通过对并发编程的三要素介绍,也就是说,要想并发程序正确地执行,必须要保证原子性、可见性以及有序性 。只要有一个没有被保证,就有可能会导致程序运行不正确 。补充volatile知识:
volatile
只保证可见性(多线程下对变量的修改是可见的)、有序性(禁止进行指令重排序)
- volatile 的底层实现原理是内存屏障(内存栅栏),Memory Barrier(Memory Fence),内存屏障会提供3个功能:
- 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成
- 它会强制将对缓存的修改操作立即写入主存
- 如果是写操作,它会导致其他CPU中对应的缓存行无效
volatile
修饰之后的变量会加入读写屏障
- 写屏障(sfence):保证在该屏障之前的,对共享变量的改动,都同步到主存当中
- 读屏障(lfence):保证在该屏障之后的,对共享变量的读取,加载的是主存中的最新数据
- 对 volatile 变量的写指令后会加入写屏障
- 对 volatile 变量的读指令前会加入读屏障
- 写屏障(sfence):保证在该屏障之前的,对共享变量的改动,都同步到主存当中
volatile
的用途像两阶段终止、单例双重锁等等:两阶段终止--volatile
@Logpublic class TwoPhaseStop {// 监控线程private Thread monitorThread;// 多线程共享变量 单线程写入(停止线程) 多线程读取 使用 volatileprivate volatile boolean stop = false;// 启动监控线程public void start() {monitorThread = new Thread(() -> {log.info("开始监控");while (true) {log.info("监控中");Thread currentThread = Thread.currentThread();if (stop) {log.info("正在停止");break;}try {log.info("正常运行");Thread.sleep(5000);} catch (InterruptedException e) {// sleep出现被打断异常后、被打断后会清除打断标记// 需要重新打断标记currentThread.interrupt();}}log.info("已停止");},"monitor");monitorThread.start();}// 停止监控线程public void stop() {stop = true;monitorThread.interrupt();}}
····下篇预告:synchronized 和 volatile 区别和底层原理
经验总结扩展阅读
- 香港北角到铜锣湾怎么走
- [CG从零开始] 4. pyopengl 绘制一个正方形
- 一文读懂Apache Geode缓存中间件
- 树马齿苋花怎么养
- 义乌到河北唐山快递要多久
- 为什么睡到半夜胃胀气
- 马栗乐真假区别?
- 韩国苏秘真假对比?
- 雨水的格言
- 跳棋怎么玩(跳棋怎么玩新手入门)