关键字volatile的主要作用是使变量在多个线程间可见。关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制,当一个变量定义为volatile,它具有内存可见性以及禁止指令重排序两大特性,为了更好地了解volatile关键字,我们可以先看Java内存模型。
1、java内存模型
java中的堆内存用来存储对象实例,堆内存是被所有线程共享的运行时内存区域,因此它存在内存可见性的问题。而局部变量方法定义的参数则不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。java内存模型定义了线程和主存之间的抽象关系:线程之间的共享变量存储在主存中,每个线程都有一个私有的本地内存,本地内存中存储了线程共享变量的副本。要注意的是本地内存是java内存模型的一个抽象,并不是真实的存在,它涵盖了缓存、写缓冲区、寄存器等区域。java内存模型控制线程之间的通信,决定了一个线程对主存共享变量的写入何时对另一个线程可见。抽象示意图如下:
线程A与线程B之间通信,要经历两个步骤:
- 线程A与线程A本地内存中更新过的共享变量刷新到主存中
- 线程B到主存中去读取线程A之前已更新过的共享变量,如:int i = 3;执行线程必须先在自己的工作线程中对变量i所在的缓存中进行赋值操作,然后再写入主存中,而不是直接把3直接写入主存。
2、原子性、可见性、有序性
1)原子性
对基本数据类型变量的读取和赋值操作是原子性操作,不可被中断,一个语句包含多个操作就不是原子性
2)可见性
指线程的可见性,一个线程修改的状态对另一个线程是可见的。volatile关键字提示线程每次都从主内存中读取变量,而不是从本地内存中读取,这样保证了同步数据的可见性。
3)有序性
java内存模型中允许编译器和处理器对指令进行重排,虽然重排过程不会影响到单线程执行的正确性,但是会影响到多线程并发执行的正确性。这时可以通过volatile来保证有序性,除了volatile,也可以通过synchronizaed和lock来保证有序性。
volatile关键字保证可见性和有序性,但不保证原子性。synchronized关键字使多个线程访问同一个资源具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能。
3、使用场景
1、状态标志
volatile boolean flag;
...
public void shutdown(){
flag = true;
}
public void doWork(){
while(flag){
//TODO
}
}
2、DCL双重检查单例模式