volatile特性
- 保证可见性
- 禁止指令重排序
- 不保证原子性
为什么保证可见性
- 定义:即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 内存模型:所有的变量都存储在主内存中。每条线程中还有自己的工作内存。线程间变量值的传递均需要通过主内存来完成。
- 当没有用volatile时:线程的执行结果写到CPU缓存中,没有马上写回到内存中,后续在某些情况下(比如CPU缓存不够用)再将CPU缓存中的值flush到内存。
- 当用volatile时:执行结果写到CPU缓存中,并且同时写回到内存。当缓存回写到内存会导致其他处理器的缓存无效。
无法解决线程安全问题
当出现多写场景时无法保证原子性
public class Test {
public volatile int inc = 0;
public void increase() {
inc++;
}
public static void main(String[] args) {
final Test test = new Test();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<1000;j++)
test.increase();
};
}.start();
}
while(Thread.activeCount()>1) //保证前面的线程都执行完
Thread.yield();
System.out.println(test.inc);
}
}
使用场景
适用于一写多读的场景,解决变量同步问题
- 状态标记量(停止线程)
volatile boolean flag = false; //线程1 while(!flag){ doSomething(); } //线程2 public void setFlag() { flag = true; }
单例模式中的双重检查锁double check
class Singleton{ private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance==null) { synchronized (Singleton.class) { if(instance==null) instance = new Singleton(); } } return instance; } }
volatile 变量和 atomic 变量、synchronized的区别
- volatile不能保证原子性
- atomic变量中的value用volatile修饰,保证了可见性和有序性,同时利用CAS的乐观锁保证了原子性
- synchronized是悲观锁,保证了原子性
- synchronized在高并发下效率比atomic低,在并发不高的情况下性能比atomic高。
volatile修饰数组
volatile修饰的变量如果是对象或数组之类的,其含义是对象获数组的地址具有可见性,但是数组或对象内部的成员改变不具备可见性
推荐阅读: