一、基础概念
在多线程并发操作中,多个线程同时对同一变量进行读写操作,很容易发生线程安全问题。Java语言提供了一种轻量级同步机制——volatile关键字,它可以确保多个线程之间对该变量进行操作时的可见性和有序性。 当一个变量被定义为 volatile 类型后,它表示该变量是“易变的”,也就是每次获取该变量的值时,都直接从内存中读取,而不是从线程的本地缓存中读取。同时,对该变量的操作不会执行处理器重排序优化,即操作时会按照程序中的顺序进行执行。
二、用途实例
1、双重检查锁定实现单例模式
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;
}
}
由于 instance 变量使用了 volatile 修饰,所以它保证了对该变量的所有操作都具有原子性和可见性,任何时刻,不管多少个线程同时访问该变量,都能保证每个线程对该变量的读取都是最新的。
2、线程间的通信
volatile 关键字可以实现线程之间的通信,即当值变化时其他线程可以立即感知到。以下代码演示使用 volatile 实现一个 boolean 类型的标志位:
public class ThreadCommunication {
private volatile boolean flag;
public void setFlag(boolean flag) {
this.flag = flag;
}
public void work() {
while (!flag) {
// 等待 flag 变为 true
}
// 执行后续操作
}
}
在多线程环境下调用 setFlag(true) 方法,即可实现线程的唤醒,并执行后续操作。
3、提高性能
在并发编程中,为了减少锁的竞争,通常会将一些变量定义为 volatile 类型。因为 volatile 变量在读写操作时,由于不涉及到锁的释放和获取,所以相对的性能更高。
三、注意事项
1、volatile 仅保证可见性和有序性,无法保证原子性。 2、volatile 对变量的写操作不依赖于变量的当前值,即多个线程对同一个 volatile 变量进行操作时,操作之间不会互相影响。 3、不要将所有变量都定义为 volatile 类型,应该根据业务需求进行选择。 4、对 volatile 变量的读写操作不能复合成更大的原子操作。 5、volatile 关键字不能保证数据的线程安全性,只能保证数据可见性。
四、总结
在多线程编程中,采用 volatile 关键字可以实现变量的可见性和有序性,从而避免出现线程安全问题。但是在使用 volatile 时,需要注意该关键字的使用场景和注意事项,以免出现思路混乱和程序错误。