volatile有什么用?为什么volatile变量的复合操作不具有安全性?volatile是怎么实现可见性和禁止重排序的(有序性)?具体的禁止重排优化实例?
volatile有什么作用?
- volatile修饰的变量能够保证可见性,volatile修饰的变量被修改后将会被强制刷新到主内存中,当某个线程读取volatile修饰的变量的时候,会将保存在该线程工作内存的变量副本清空,强制从主内存中读取该变量新的值
- volatile禁止重排序优化,volatile修饰的变量的写操作总是发生在对volatile变量的读操作之前
volatile变量的复合操作为什么不具有安全性?
public class VolatileVisibility{ public static volatile int i=0; // volatile修饰的变量 public static void increase(){ i++; // volatile修饰的变量的复合操作 } }
需要注意的是volatile修饰的变量的可见性指的是volatile变量的写操作,仅仅指的是单个的写操作而不是对volatile变量的复合操作,复合操作不具有安全性(因为复合操作是分两步完成的,线程可以在两步中间访问,不具有原子性)
这种情况下要保证线程安全,increase()方法需要使用synchronized进行修饰,synchronzed具有原子性和可见性,因此不需要使用volatile对变量进行修饰保证可见性
volatile如何实现禁止重排序
volatile禁止指令重排序,避免多线程环境下程序出现乱序执行的现象(乱序执行影响可见性和有序性)
- 一方面happpen-before原则中的volatile原则表明volatile修饰的变量的写操作总是发生在volatile修饰的变量的读操作之前,保证了有序性
- 另一方面因为编译器和处理器会进行指令重排序,通过在指令序列中插入内存屏障指令禁止在内存屏障指令前后的指令执行重排序优化。(等同于编译器重排序规则和
处理器重排序规则),内存屏障指令的另一个作用是强制将cpu缓存中的数据刷新到主内存中,保证其他cpu读到的数据都是最新的版本,保证了可见性。
总结:volatile具有的可见性和有序性实际上是通过一部分的happen-before规则中的volatile规则,一部分是通过在执行序列中插入的内存屏障序列完成的。
volatile禁止指令重排序的实例
public class DoubleCheckLock{ private static DoubleCheckLock instance; private DoubleCheckeLock(){ } public static DoubleCheckLock getInstance(){ if(instance==null){ synchronzied(DoubleCheckLock.class){ if(instance==null){ instacnce=new DoubleCheckLock(); } } } } }
这就是一个单例双重检测的代码,这段代码在单线程环境下没有什么问题
但是在多线程中可能就出现问题,**因为某个线程执行到第一次检测的时候,读取到的instance不为null时候,instance的引用对象可能没有完成初始化,**因为instance=new DoubleCheckLock()可以分为3步完成
分配内存空间 memory = allocate() 初始化对象 instance(memory) 设设置引用变量指向对象的地址 instance = memory
但是由于初始化对象和设置执行内存地址可能会重排序,
分配内存空间 memory = allocate() 设设置引用变量指向对象的地址 instance = memory 初始化对象 instance(memory)
因为初始化对象和设置引用变量指向内存地址之间没有数据依赖性,在单线程中重排和不重排并不影响结果,因此这种指令重排序是允许的(指令重排对单线程来说能够保证语义一致性,但是对多线程的语义一致不能保证),因此,带来问题,当一条线程访问instance不为null的时候,此时instance实例不一定已经初始化完成了,就造成了线程安全问题
此时,使用volatile禁止instance变量被执行指令重排序 private volatile static DoubleCheckLock instance;
**总结:**双重检测的单例,在单线程情况下允许指令重排序,指令重排序也会保证内存语义一致,但是在多线程下不能保证内存语义一致性,因为创建一个对象实例的时候会经过三个步骤,一条线程访问instance不为null并不能说明该实例已经被初始化完成了,使用volatile修饰声明的引用变量,禁止指令重排优化
- php配合jquery实现增删操作具体实例
- 【MMAP】认真分析mmap:是什么 为什么 怎么用-sqlite实现原理
- 关于volatile的可见性和禁止指令重排序的疑惑
- Java volatile 怎么保证不被指令重排序优化
- 什么是ifttt,ifttt怎么玩? ifttt操作体验具体步骤
- 李砍柴:通过写作真的能实现财富自由么?具体应该怎么操作?
- jQuery简单实现对数组去重及排序操作实例
- 什么是ifttt,ifttt怎么玩? ifttt操作体验具体步骤
- PHP+MySQL实现对一段时间内每天数据统计优化操作实例
- php配合jquery实现增删操作具体实例
- php配合jquery实现增删操作具体实例
- 前端性能优化--为什么DOM操作慢? 浅谈DOM的操作以及性能优化问题-重绘重排 为什么要减少DOM操作 为什么要减少操作DOM
- 轻量级的同步机制——volatile语义详解(可见性保证+禁止指令重排)
- php配合jquery实现增删操作具体实例
- Java实现按中文首字母排序的具体实例
- 所谓的优化内页是怎么操作的呢?SEO在编辑文章时候都做了些什么?
- 什么是ifttt,ifttt怎么玩? ifttt操作体验具体步骤
- 什么是ifttt,ifttt怎么玩? ifttt操作体验具体步骤
- 什么是ifttt,ifttt怎么玩? ifttt操作体验具体步骤
- Java实现按中文首字母排序的具体实例