一、简介(请你谈谈对volatile的理解)
首先,volatile是Java虚拟机提供的轻量级的同步机制,他基本遵守了JMM的规范。
JMM模型:JMM内存模型描述
二、三大特性
volatile主要有三大特性:保证可见性,有序性(即禁止指令重排序),但是不保证原子性。
什么叫保证了可见性?
可见性:一个线程修改了主物理内存的值,其他线程马上获取主内存的值。
禁止指令重排序
有序性:为了提高性能,编译器和处理器都会对指令做重排,一般分为:
源代码-> 编译器优化的重排->指令并行的重排->内存系统的重排->最终执行的指令
单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
处理器在进行重排序时必须要考虑指令之间的数据依赖性
多线程环境中线程是交替执行的,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。加入了volatile之后,就可以保证有序性。
不保证原子性
验证不保证原子性代码:
public class volatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for(int j=1;j<=1000;j++){
myData.addPlusPlus();
myData.addMyAtomic();
}
},String.valueOf(i)).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"\tAtomicInteger type,final number"+myData.atomicInteger);
}
}
class MyData{
volatile int number = 0;//没有使用volatile,则最后结果不会是20000
public void addT060(){
this.number =60;
}
public void addPlusPlus(){
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addMyAtomic(){
atomicInteger.getAndIncrement();
}
}
如何解决?
1、加synchonized关键字;
分析:过重,不适合。
2、直接使用juc下AtomicInteger。
atomicInteger.getAndIncrement(); 解决了多线程环境下number++的问题;底层原理是CAS。
CAS原理:CAS原理描述
三、哪里有用过volatile?
单例模式DCL(Double Check Lock双端检锁机制),DCL机制不一定线程安全,原因是指令重排序的存在,加入volatile可以禁止指令重排。
原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。
Instance = new SingletonDemo();可以分为以下三步完成。
扫描二维码关注公众号,回复:
8902975 查看本文章
Memory = allocate(); //1.分配对象内存空间
Instance(memory); //2.初始化对象
Instance = memory;//3.设置instance指向刚分配的内存地址,此时instance!=null
2和3不存在依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变。