前言
本篇文章主要记录在读完《深入理解Java虚拟机》中对volatile关键字的用法后,进行个人消化后的总结和代码实践。
一、volatile关键字的语义?
- 可见性,子线程修改变量,值立即回写到主内存,其他线程不读取副本而是直接从主内存读取新值。
- 禁止指令优化重排序。
二、举例实践
1.可见性
代码如下(示例):
public class Demo {
private static volatile boolean flag = false;
public static ExecutorService executor = Executors.newFixedThreadPool(20);
public static void main(String[] args) throws Throwable{
for (int i = 0; i <100 ; i++) {
executor.submit(new Thread(new Runnable() {
@Override
public void run() {
while (!flag){
}
}
}));
}
//保证线程都启动完毕
Thread.sleep(500);
flag = true;
executor.shutdown();
executor.awaitTermination(100, TimeUnit.SECONDS);
System.out.println("完成!");
}
变量flag用volatile声明,程序会正常结束,打印“完成!”。
变量flag不用volatile声明,程序无法结束。
2.DCL单例模式中禁止指令重排序
代码如下(示例):
public class Singleton {
private static volatile Singleton instance=null;
public static Singleton getInstance() {
if(null == instance){
System.out.println("1");
synchronized (Singleton.class){
if(null == instance){
System.out.println("2");
instance = new Singleton();//如果不用volatile修饰instance,这一步在编译成字节码后,很有可能发生指令重排序,造成返回一个未初始化完成的对象。
}
}
}
return instance;
}
}
并发量到一定地步可以模拟返回一个未初始化完成的对象现象 ,本机模拟不到。
这个现象涉及到java内存模型中对象的创建语句在字节码中指令重排序。
总结
虽然掌握了volatile一些常用场景的用法,和一些发生线程非安全的原因,但是要彻底掌握,需要对java内存模型,对象创建,以及对虚拟机字节码重排序有深入了解,在今后的工作中,如果有时间再补充这块知识进行加深印象。