volatile
是 Java 中的关键字,主要用于确保多线程环境下共享变量的可见性、禁止指令重排序以及一些轻量级的同步操作。
volatile
变量进行写操作时,这个变量的新值会立即被刷新到主内存,而不是在线程本地缓存中等待一段时间。volatile
关键字禁止指令重排序,确保在执行到 volatile
变量的读操作之前,所有的写操作都已经完成。这可以防止出现一些意外的程序行为。volatile
保证可见性和禁止指令重排序,但并不保证对 volatile
变量的所有操作都是原子性的。如果一个变量的操作需要保证原子性,需要使用 synchronized
或其他锁机制。synchronized
关键字,volatile
是一种轻量级的同步机制。它适用于那些对变量的写操作不依赖于变量的当前值的场景。实例解析volatile的作用:
public class Singleton {
/**
** 正常实例化对象过程:
分配内存空间。
初始化对象。
将内存空间的地址赋值给对应的引用。
** 操作系统可以对对象实例化过程进行优化:
分配内存空间。
将内存空间的地址赋值给对应的引用。
初始化对象
多线程环境下就可能将一个未初始化的对象引用暴露
*/
// 可见性,在竞态条件的情况下,其中一个线程初始化instance的时候,instance会及时的刷新主内存
// 防止指令重排序 防止多线程环境下就可能将一个未初始化的对象引用暴露
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getSingleton() {
// 第一次检查,如果实例为空才进入同步块
if (null == instance) {
synchronized (Singleton.class) {
// 第二次检查,如果实例为空才进入同步块
// 在instance已初始化成功。已有线程已经阻塞在同步代码块外面,第一个线程释放锁,第二个线程立马获取锁成功
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile
变量时,JVM 会将该变量的值立即刷新到主内存,而不是缓存在线程的本地工作内存中。volatile
关键字通过插入内存屏障(memory barrier)来禁止指令重排序。内存屏障分为写屏障和读屏障:
volatile
变量之前,会插入写屏障,确保所有之前的操作都完成。volatile
变量之后,会插入读屏障,确保所有后续的操作都在读取之后执行。volatile
变量的操作在其他操作之前完成,读取 volatile
变量的操作在其他操作之后进行。volatile
变量之前插入写屏障。volatile
变量之后插入读屏障。
- 可见性需求:
- 如果一个变量的值在一个线程中发生了改变,其他线程需要立即看到这个变化,那么可以考虑使用
volatile
。- 无依赖性:
volatile
适用于对变量的写操作不依赖于当前值的场景。如果写操作依赖于当前值,考虑使用其他同步机制,如synchronized
。- 不涉及复合操作:
volatile
适用于单个变量的读写操作,但不适用于复合操作,即一系列操作的原子性需求。- 不需要维持不变性条件:
- 如果多个变量之间有复杂的不变性条件需要维护,考虑使用其他同步机制。
总的来说,
volatile
主要用于简单的状态标记、标志位等情况,不适用于复杂的原子性操作。在需要保证一系列操作的原子性时,应该考虑使用更强大的同步机制,比如synchronized
。
volatile
关键字通常用于确保在多线程环境中对共享变量的可见性和禁止指令重排序。
public class SharedResource {
private volatile boolean flag = false;
public void setFlag() {
// 一些操作
flag = true; // 将共享变量标记为true
}
public void doSomething() {
while (!flag) {
// 循环等待,直到共享变量flag变为true
}
// 执行操作,共享变量flag此时可见
}
}
volatile
可以确保对 flag
的修改对于其他线程是可见的,避免了循环等待线程陷入死循环的情况。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在多线程环境中,使用 volatile
修饰的 instance
变量确保了对单例对象的初始化在多线程环境中的正确性。
public class Counter {
private volatile int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
对计数器的读写操作都是原子的,但使用 volatile
可以确保对计数器的修改对于其他线程是可见的。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
通过 volatile
保证了在多线程环境中对 instance
的读写操作的可见性。
public class Resource {
private static volatile Resource instance1 = new Resource();
private static volatile Resource instance2 = new Resource();
private Resource() {}
public static Resource getInstance1() {
return instance1;
}
public static Resource getInstance2() {
return instance2;
}
}
使用 volatile
避免了双检锁的死锁问题,确保在多线程环境中正确获取实例。