一、什么是Volatile关键字
Volatile关键字用于表明变量可能会在任意时刻被修改,它告诉编译器不要优化处理这个变量,而是每次都从内存中读取它的值。Volatile关键字本质上是告诉编译器,这个变量的值可能会发生变化,所以在读取这个变量的时候必须从内存中重新获取,而不是使用缓存中的副本。
二、Volatile关键字的使用场合
1、多线程访问同一个变量。
public class VolatileExample { private volatile boolean flag = false; public void setFlag() { flag = true; } public void whileFlag() { while (!flag) { // do something } } }
在这个例子中,如果不加volatile关键字,一个线程设置flag值为true时,其他线程不一定能够立即看到flag值的变化,从而有可能导致死循环。加上volatile关键字后,写操作会立即刷新到主内存中,读操作也会直接从主内存中获取最新的值。
2、操作系统中的中断(interrupt)。
private volatile boolean isRunning = true; public void run() { while (isRunning) { // do something } } public void stop() { isRunning = false; }
在这个例子中,如果不加volatile关键字,当stop方法把isRunning设置为false时,run方法中的while循环有可能无法及时退出。
三、Volatile关键字的实现原理
Java中的Volatile关键字通过禁止JVM对变量的操作进行重排序来保证线程的可见性。
根据volatile变量的定义,对该变量的修改会立即刷新到主内存中,而在线程读取该变量时,会直接从主内存中获取最新的值。但是,由于程序的运行和处理过程不可控,JVM含有重排序策略,会在执行代码时对其进行优化。在需要提高CPU运行效率或减小内存访问延迟的情况下,会把代码中访问内存的顺序打乱,以便提高性能。但是,这种优化可能导致多线程访问共享内存时出现异常,因为多线程访问时共享内存在各个线程之间不可见。
为了解决这个问题,Java使用了内存屏障机制,来禁止对volatile变量操作语义重排。
private static volatile int i = 0; public static void main(String[] args) { while (i == 0) { // do something } }
例如,在上述例子中,如果不加volatile关键字,代码可能会无限循环,而加上volatile关键字后,JVM会在while循环之前插入一个内存屏障,保证i的修改操作先于while循环的读操作。
四、Volatile关键字的注意事项
1、Volatile不能保证原子性。当多个线程在同时修改一个volatile变量的值时,可能会出现数据不一致的情况。解决这个问题的办法是使用synchronized或Atomic类。
2、Volatile关键字应该只用于一些单一的、轻量级的任务。如果需要数据同步或者加锁等重量级的操作,应该使用synchronized或Lock。
3、Volatile变量的读取和修改操作一般情况下耗时较长,建议在高并发情况下使用局部变量代替volatile变量。
五、使用Volatile关键字的代码示例
public class VolatileExample { private volatile int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }
在这个例子中,使用Volatile关键字保证多个线程操作count变量的可见性。