-
- 作用:保证数据的可见性,以及确保变量不会被重排序
volatile关键字修饰的成员变量,不存在工作线程的副本;每次直接都从主内存中读取。只能保证数据的可见性,但不保证操作的原子性。
-
- 内存可见性
写一个volatile变量时,JMM首先修改工作内存中的变量值,并刷新到主内存中。
读一个变量时,JMM会把该线程对应的本地内存置为无效,并从主内存中读取共享变量。
对volatile变量的读写,可以说都是直接对主内存进行的操作,这样虽然会牺牲一些性能,但是解决了“缓存一致性问题”,使得变量在多线程间的可见性得到了很好的保证。
-
- 禁止指令重排序-内存屏障
Singleton instance= new Singleton();
编译出的字节码指令可以用如下三行表示:
1.为对象分配内存空间2.初始化对象 3.将INSTANCE变量指向刚分配的内存地址
由于步骤2,3交换不会改变单线程环境下的执行结果,故而这种重排序是被允许的。
故在多线程下可能存在获取的instance==null的情况
instance变量加个volatile关键字,这样编译器就会根据一定的规则禁止对volatile变量的读写操作重排序。而编译出的字节码,也会在合适的地方插入内存屏障,比如volatile写操作之前和之后会分别插入一个StoreStore屏障和StoreLoad屏障,禁止CPU对指令的重排序越过这些屏障。
-
- 变量的读写,操作的读写
对volatile变量的读写具有原子性,但是其他操作并不一定具有原子性,一个简单的例子就是i++。由于该操作并不具有原子性,故而即使该变量被volatile修饰,多线程环境下也不能保证线程安全。
-
- 使用案例
- 和Synchronized结合,用于对变量n读多写少的情况
- 使用案例
告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。
-
-
- 和CAS结合保证操作的原子性,如AtomicInteger
-
-
-
- 修饰线程之间的共享变量
-
如下图程序,运行时会一直卡在循环中。即子线程中修改主存后,但是main线程读取的是当前线程存储空间的值,导致循环不能停止。添加volatile修饰后,在子线程运行结束后,主线程会退出循环。
-
- 注意事项:本身不具备线程安全的特性