0. volitile作用
1. 保证线程可见
2. 防止指令重排
变量 -> 内存 -> cpu缓存 -> cpu
volitile 通过缓存一致性协议实现多线程,缓存一致性协议保证同一时刻只能修改一个缓存区域的同一变量。
1. 被volatile修改的变量有2大特点
1.1. 特点: 可见性、有序性、不保证原子性。即(保证线程可见,防止指令重排)
VS JMM的特点:可见性、有序性、原子性
1.2. volatile的内存语义
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量
所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取
变量 -> 内存 -> cpu缓存 -> cpu
volitile 通过缓存一致性协议实现多线程,缓存一致性协议保证同一时刻只能修改一个缓存区域的同一变量。
2. 内存屏障
2.1. 什么是内存屏障
①. 内存屏障(也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。内存屏障其实就是一种JVM指令,Java内存模型的重排规则会要求Java编译器在生成JVM指令时插入特定的内存屏障指令,通过这些内存屏障指令,volatile实现了Java内存模型中的可见性和有序性,但volatile无法保证原子性
②. 内存屏障之前的所有写操作都要回写到主内存
内存屏障之后的所有读操作都能获得内存屏障之前的所有写操作的最新结果(实现了可见性)
③. 一句话:对一个volatile域的写, happens-before于任意后续对这个volatile域的读,也叫写后读
2.2. 内存屏障分类
2.2.1 粗分两种

2.2.2 细分四种

2.3 内存屏障禁止指令重排

2.3.2 volitile读

2.3.2 volitile写

3. volitile使用场景

public class SafeDoubleCheckSingleton{
//通过volatile声明,实现线程安全的延迟初始化。
private volatile static SafeDoubleCheckSingleton singleton;
//私有化构造方法
private SafeDoubleCheckSingleton(){
}
//双重锁设计
public static SafeDoubleCheckSingleton getInstance(){
if (singleton == null){
//1.多线程并发创建对象时,会通过加锁保证只有一个线程能创建对象
synchronized (SafeDoubleCheckSingleton.class){
if (singleton == null){
//隐患:多线程环境下,由于重排序,该对象可能还未完成初始化就被其他线程读取
//原理:利用volatile,禁止 "初始化对象"(2) 和 "设置singleton指向内存空间"(3) 的重排序
singleton = new SafeDoubleCheckSingleton();
}
}
}
//2.对象创建完毕,执行getInstance()将不需要获取锁,直接返回创建对象
return singleton;
}
}
4. 最终总结

本文深入解析了Java中的volatile关键字,探讨其如何确保线程间的可见性和有序性,避免指令重排序,并通过实例展示了其在单例模式中的应用。
526

被折叠的 条评论
为什么被折叠?



