Java 内存模型要求,变量的读取操作和写入操作都必须是原子操作,但对于非 volatile
类型的 long
和 double
变量,JVM 允许将 64 位的读操作或写操作分解为两个 32 位的操作。
当读取一个非 volatile
类型的 long
变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高 32 位和另一个值的低 32 位。
当把变量声明为 volatile
类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
volatile
变量不会被缓存在寄存器或者对其他处理器不可见的地方,因为在读取 volatile
类型的变量时总会返回最新写入的值。
volatile
变量通常用做某个操作完成、发生中断或者状态的标志。
volatile
变量只确保可见性,不确保原子性。
当且仅当满足以下所有条件时,才应该使用 volatile
变量:
- 对变量的写入操作不依赖变量的当前值
- 该变量不会与其他状态变量一起纳入不变性条件中
- 在访问变量时不需要加锁
ThreadLocal
提供了 get
与 set
等访问接口和方法,这方法为每个使用该变量的线程都存有一份独立的副本,因此 get
总是返回由当前执行线程在调用 set
时设置的最新值。
ThreadLocal
对象通常用于防止对可变的单实例变量或全局变量进行共享。
final
类型的域是不能修改的,但是如果 final
域所引用的对象是可变的,那么这些被引用的对象是可以修改的。
在 Java 内存模型中,final
域能确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享这些对象时无须同步。