文章目录:java
1.简单理解Volatile和synchronized缓存
2.Volatile详解多线程
3.synchronized详解app
4.Volatile与synchronized的区别与联系jvm
1.简单理解Volatile和synchronized:优化
synchronized:当被synchronized修饰时,synchronized会锁定当前变量,只有当前线程可以访问该变量,其余线程会被阻塞。spa
volatile:当volatile修饰变量时,编译器对此变量的读写操做不作任何优化,每次都会去内存去读,写也是写到内存,不作任何的缓存优化线程
volatile,final,synchronized均可以实现可见性。code
2.Volatile详解对象
volatile可见性原理
可见性:任何线程能立刻看到它的结果
volatile的本质是告诉jvm这个变量是在寄存器中的值是不肯定的,加入volatile关键字之后,保存数据会同步到主内存中,而另外一端读数据时也会从主内存中读取,volatile使用cpu的缓存锁来保证可见性。
volatile禁止指令重排序
指令重排序的目的: 提升cpu运算效率
cpu提升利用率的过程:进程 -> 线程 -> 指令
指令重排序究竟是什么?
举个栗子:写代码每一行都是一个指令,有的指令运行时间比较长,不能让其余指令排队等着,这时对指令进行从新排序,来提升运行效率

无论怎么重排序,单线程都遵循 as-if-serial语义,而多线程遵循 happen-before
讲清楚指令重排序了,volatile如何防止指令重排?
Volatile变量在赋值后会有一个lock add命令,这个命令至关于内存屏障,重排序时不能把屏障后的指令重排序到屏障以前。
volatile使用案例
案例1
在下面这个单例模式下,会指令重排序,若是这时多线程访问,这时尚未初始化对象,会发生不完整实例。 如图2-1
使用volatile修饰 instance 防止这种问题发生
public class VolatileDemo { private volatile static VolatileDemo instance; private VolatileDemo(){} /* * 懒汉模式 double check * 不完整实例,指令重排序致使多线程访问发生不完整实例, * 解决办法: private volatile static VolatileDemo instance; * 防止指令重排序 * */ public static VolatileDemo getInstance2(){ if (instance == null ){ synchronized (VolatileDemo.class){ if (instance == null){ instance = new VolatileDemo(); } } } return instance; } }
图2-1
案例2
volatile int i; public void add{ i++; }
这里先要读i值 ,而后++,又同步。 再多线程操做时会出错,原理同案例1,有关指令重排序的问题致使的。
3.synchronized详解
当被synchronized关键字修饰的代码块在被编译成字节码的时候,会在该代码块开始和结束的地方加入 monitorenter 和 moniterexist 指令,任何对象都有一个monitor相关联,当一个monitor被持有后,他就处于锁定状态,当线程执行到monitorenter指令时,会获尝试获取对象对应的monitor的全部权,即获取对象的锁。monitor指令如图3-1
图3-1
虚拟机在执行这两个命令的时候,会检查对象的锁状态是否为空或当前线程是否已经拥有对象的锁,
若是是 则对象锁的计数器加 1 ,直接进入同步代码,
若是不是,则当前线程阻塞等待,等待锁释放。
4.Volatile与synchronized的区别与联系
(1)volatile,final,synchronized均可以实现可见性
(2)volatile的本质是告诉jvm这个变量是在寄存器中的值是不肯定的,须要从主存中读取。
(3)synchronized是锁定当前变量,只有当前线程可以访问该变量,其余线程被阻塞。
(4)volatile仅能实现变量的修改可见性,不具有原子性,而synchronized能够实现变量的修改可见性和原子性
(5)volatile标记的变量不会被编译器优化,而synchronized能够(禁止指令重排序)
(6)volatile使用在变量级别,而synchronized可使用在变量和方法
(7)volatile不会阻塞线程,而synchronized可能会