volatile是java虚拟机提供最轻量级的同步机制。
volatile两个特性:1,保证同步的变量对所有线程是可见的。虽然对所有线程是即时可见的,但是却不保证原子性,也就是不保证线程安全,比如对于创建20个线程,每个线程都执行i++操作,执行100次,但是i输出的结果小于2000。因为一条i++用javap反编译是由4条指令来执行的。所以我们通过synchronized来保证原子性。 下面给出一个volatile适用的场景: 当shutdown()方法被执行时,保证所有线程中执行dowork()方法都立即停下来。
1 volatile boolean shutdownRequested;2 public void shutdown(){3 shutdownRequested = true;4 }5 public void dowork(){6 while(!shutdownRequested){7 ``````````8 }9 }
2,禁止指令重排序优化。
先给出一个简单的例子
1public class Singleton{ 2 private volatile static Singleton instance; 3 public static Singleton getInstance(){ 4 if(instance == null){ 5 synchronized(Singleton.class){ 6 if(instance==null){ 7 instance = new Singleton(); 8 } 9 } 10 } 11 return instance;12 } 13 }14 public static void main(String[] args){15 Singleton.getInstance();16 }
其中instance变量被赋值的部分执行了“lock addl $0x0,(%esp)”操作,这个操作相当于一个内存屏障(memory Barrier),重排序后的指令不能放到内存屏障之前的位置。
对原子性,可见性与有序性理解:
java内存模型是围绕着在并发过程中如何处理原子性、可见性与有序性这三个特性来建立的。
原子性:原子性操作包括,read、load、assign、use、store、和write。lock和unlock、synchronized块之间的操作也是原子性。
可见性:volatile,当一个线程修改了共享变量的值,其他线程能够立即得到这个修改的值。synchronized在变量执行unlock操作之前,必须先把此变量同步回主内存中和finally被finally修饰的字段在构造器中一旦初始化完成,并且构造器没有把this的引用传递出去,那在其他线程中都能看到finally字段。
有序性:volatile本身就有禁止指令重排序的语义,而synchronized则是由“一个变量在同一时刻只允许一个线程对其执行unlock操作”。
我们衡量并发线程安全的时候不要受到时间顺序的干扰,一切必须以,先行发生原则为准。