Java中volatile修饰引用类型
在Java中,volatile
关键字用于修饰变量,用来保证变量在多线程环境下的可见性和顺序性。当我们使用volatile
修饰引用类型时,实际上是修饰引用变量本身,而非对象。
1. 引用类型的可见性问题
在多线程环境下,线程之间共享的变量存储在主内存中,每个线程都有自己的工作内存。当一个线程修改了共享变量的值时,其它线程无法立即感知到这个修改,因为每个线程都有自己的工作内存,线程之间的变量值不会自动同步。
这就导致了一个可见性问题:如果一个线程修改了共享变量的值,其它线程如何能够及时地获取到这个新的值呢?这时候就需要使用volatile
关键字。
2. volatile关键字的作用
volatile
关键字保证了被修饰的变量对所有线程的可见性。当一个线程修改了volatile
变量的值时,JVM会立即将该值刷新到主内存中,而其它线程在读取该变量时,会直接从主内存中获取最新的值。
除了可见性,volatile
关键字还保证了一定的顺序性。在一个线程内,所有的操作都是有序执行的,但在一个线程的不同操作间的顺序,并没有严格的限制。而通过使用volatile
关键字,可以保证某个操作的执行顺序符合我们的预期。
3. volatile修饰引用类型的问题
当我们使用volatile
修饰引用类型时,实际上是修饰引用变量本身,而非对象。也就是说,volatile
修饰的是引用地址,而不是对象本身。
public class VolatileExample {
private volatile MyClass myClass = new MyClass();
public void updateMyClass() {
myClass = new MyClass(); // 创建了一个新的对象
// 其它操作
}
public void doSomething() {
MyClass localClass = myClass; // 读取引用变量到本地变量
// 其它操作
}
// ...
}
在上述代码中,VolatileExample
类中的myClass
变量是一个引用类型,并且使用volatile
关键字修饰。当调用updateMyClass
方法时,会创建一个新的MyClass
对象,并赋值给myClass
变量。而在doSomething
方法中,会将myClass
的值读取到本地变量localClass
中。
如果没有使用volatile
关键字修饰myClass
变量,那么在doSomething
方法中读取到的myClass
值可能是过期的,因为其它线程可能已经修改了myClass
的值。
但是,使用volatile
修饰引用类型时,并不能保证引用类型所指向的对象的可见性。也就是说,虽然一个线程修改了引用变量的值,但是其它线程并不会立即感知到这个修改。只有在获取了新的引用变量后,才能读取到新的对象。
4. 示例代码及分析
下面我们通过一个示例代码来进一步理解volatile
修饰引用类型的作用。
public class VolatileExample {
private volatile MyClass myClass = new MyClass();
public void updateMyClass() {
myClass.setValue(10);
}
public void doSomething() {
MyClass localClass = myClass;
System.out.println(localClass.getValue()); // 输出:0
}
public static void main(String[] args) {
final VolatileExample example = new VolatileExample();
Thread threadA = new Thread(() -> {
example.updateMyClass();
});
Thread threadB = new Thread(() -> {
example.doSomething();
});
threadA.start();
threadB.start();
}
}
class MyClass {
private int value;
public int getValue() {
return value;
}
public