[超级链接:Java并发学习系列-绪论]
volatile关键字在之前的章节中多次提及:
本章主要就volatile关键字的两种实际用法进行说明。
本文只对volatile常见的两种用法进行学习:
应用场景:使用一个布尔变量来标记状态,用于指示发生了一件重要的一次性事件。例如:标志配置完成了初始化、标志某种服务关闭了服务等等。
编码场景:
状态标志位:
/**
* 是否关闭
*/
private static boolean shutdown = false;
咖啡机:
/**
* Title: 咖啡机
*
* @author 韩超 2018/3/16 13:58
*/
static class CoffeeMaker {
/**
* 关闭关闭咖啡机
*/
public static void shutdown() {
shutdown = true;
System.out.println("关闭了咖啡机...");
}
/**
* 生成开发
*/
public static void makeCoffee(String name) {
System.out.println("咖啡机开始为客户制作咖啡...");
while (!shutdown) ;
System.out.println("咖啡机已经停止工作,不再对外提供服务!");
}
}
制作咖啡与关闭咖啡机:
//开始制作咖啡
new Thread(() -> {
CoffeeMaker.makeCoffee(Thread.currentThread().getName());
}).start();
Thread.sleep(100);
//关掉咖啡机
new Thread(() -> {
CoffeeMaker.shutdown();
}).start();
运行结果:
咖啡机开始为客户制作咖啡...
关闭了咖啡机...
结果分析:
将上面的代码做一个很小的修改:将是否关闭这个状态用volatile关键字标记:
/**
* 是否关闭
*/
private volatile static boolean shutdown = false;
然后再次运行测试代码,运行结果如下:
咖啡机开始为客户制作咖啡...
关闭了咖啡机...
咖啡机已经停止工作,不再对外提供服务!
使用一次性状态标志的关键点:
可以看到,上述两个关键点,其实就是对原子性和有序性的保证。
也就是说,如果要是用volatile进行并发编程,就需要通过其他手段来保证代码的原子性和有序性。
双重检查是一种单例模式的实现方式,关于单例模式这里不多做介绍。
/**
* 双重检测单例模式--不加volatile关键字
*
* @author hanchao 2018/3/17 19:14
**/
static class DoubleCheckSingleton {
private static DoubleCheckSingleton instance = null;
private DoubleCheckSingleton() {
}
public static synchronized DoubleCheckSingleton getInstance() {
if (instance == null) {//0.null判断
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
//耗时较长的初始化操作
instance = new DoubleCheckSingleton();//1.初始化 2.对象地址指向引用
}
}
}
return instance;//3.返回对象
}
}
这种模式存在问题:
这个操作可以划分为:
这两步操作经过指令重拍之后可能是2->1的顺序,因为在单线程中1->2和2->1的执行结果时一样的。
这种指令重拍在单线程下毫无问题,但是在多线程下可能存在问题:
instance.getName()
等操作。instance.getName()
等操作,会报NullPointerException
异常。java的内存模式在持续改进之中,在jdk5中提到:
Updates for J2SE 5.0 (aka 1.5, Tiger)
In particular, double-check idioms work in the expected way when references are declared volatile.
也就是说:在JDK1.5及以后的版本中,通过将对象引用声明成volatile的,是可以正常使用双重检查模式的。
添加volatile的双重检查模式的代码如下:
/**
* 双重检测单例模式--加volatile关键字
*
* @author hanchao 2018/3/17 19:10
**/
static class DoubleCheckedVolatileSingleton {
//注意这里是volatile的
private volatile static DoubleCheckedVolatileSingleton instance = null;
public static DoubleCheckedVolatileSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedVolatileSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedVolatileSingleton();
}
}
}
return instance;
}
}
[1] Java并发编程:volatile关键字解析
[2] Java 中的双重检查(Double-Check)
[3] 双重检查锁失效是因为对象的初始化并非原子操作?