(一)volatile的使用
1、使用场景
(1)状态标识。用于实时指示某个重要性事件的发生,比如完成初始化或者停机。
volatile boolean toShutdown;
......
public void shutdown() { toShutdown = true; }
public void doWork() {
while (!toShutdown) {
// 执行操作
}
}
(2)对象安全发布。volatile是处理对象安全发布的其中一种方式,更多内容参加这里。
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变量不会导致new对象的时候对象未初始化完成。
2、通常来说,使用volatile必须具备以下2个条件:
(1)对变量的写操作不依赖于当前值
(2)该变量没有包含在具有其他变量的不变式中
对于1,因为volatile变量不保证原子性,一个线程执行i++的同时另一个线程获取i的值,不一定能获取到最新值。
对于2,
volatile int min = 0;
volatile int max = 10;
public boolean setA(int newMin) {
if (newMin > max) {
return false;
}
min = newMin;
return true;
}
public boolean setB(int newMax) {
if (newMax < min) {
return false;
}
max = newMax;
return true;
}
一个线程调用setA(8),一个线程调用setB(2),可能会得到min=8、max=2,这与最初代码设计意图不符合。
(二)原理分析
volatile实现内存可见性原理:
volatile Singleton instance = new Singleton();
instance变量在进行写操作的时候,汇编下增加了lock指令。
lock指令的作用:
1.线程工作内存中的数据在进行写操作的时候,会立即回写到主内存中
2.回写到主内存时,让其他CPU中该数据的缓存失效,下次读取需要重新从主内存中获取
/**
* Volatile的使用测试
*
* @author peter_wang
* @create-time 2015-1-14 下午10:11:57
*/
public class ThreadVolatileDemo {
private boolean isStop = false;
private void changeStatus() {
isStop = !isStop;
}
/**
* @param args
*/
public static void main(String[] args) {
final ThreadVolatileDemo test = new ThreadVolatileDemo();
Thread thread1 = new Thread() {
@Override
public void run() {
System.out.println("尝试退出开始");
while (true) {
//一直检测isStop变量是否更新
if (test.isStop) {//A1
System.out.println("退出成功");
System.exit(0);
}
}
}
};
thread1.start();
try {
Thread.sleep(3000);
test.changeStatus();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
A1部分不断循环重复读取isStop值,但是读取的是线程工作内存中的数据,而且不断命中该数据,数据块不易被替换,增加读取到旧值的概率。修改isStop变量为
volatile,变量被修改后会立即回写到主内存,A1读取线程工作内存失效,从主内存中读取到最新值。