X Tutup
# 02-volatile原理 ## volatile synchronized 在多线程场景下存在性能问题 而 `volatile` 关键字是一个更轻量级的线程安全解决方案 volatile 关键字的作用:保证多线程场景下变量的可见性和有序性 - 可见性:保证此变量的修改对所有线程的可见性。 - 有序性:禁止指令重排序优化,编译器和处理器在进行指令优化时,不能把在 volatile 变量操作(读/写)后面的语句放到其前面执行,也不能将volatile变量操作前面的语句放在其后执行。遵循了JMM 的 happens-before 规则 线程写 volatile 变量的过程: 1. 改变线程本地内存中volatile变量副本的值; 2. 将改变后的副本的值从本地内存刷新到主内存 线程读 volatile 变量的过程: 1. 从主内存中读取volatile变量的最新值到线程的本地内存中 2. 从本地内存中读取volatile变量的副本 ### volatile 实现原理 如果面试中问到了 volatile 关键字,应该从 Java 内存模型开始讲解,再说到原子性、可见性、有序性是什么 之后说 volatile 解决了`有序性`和`可见性`,但是并不解决`原子性` volatile 可以说是 Java 虚拟机提供的最轻量级的同步机制,在很多开源框架中,都会大量使用 volatile 保证并发下的有序性和可见性 > volatile 实现 `可见性`和 `有序性` 就是基于 `内存屏障` 的: 内存屏障是一种 `CPU 指令`,用于控制特定条件下的重排序和内存可见性问题 - 写操作时,在写指令后边加上 store 屏障指令,让线程本地内存的变量能立即刷到主内存中 - 读操作时,在读指令前边加上 load 屏障指令,可以及时读取到主内存中的值 JMM 中有 4 类`内存屏障`:(Load 操作是从主内存加载数据,Store 操作是将数据刷新到主内存) - LoadLoad:确保该内存屏障前的 Load 操作先于屏障后的所有 Load 操作。对于屏障前后的 Store 操作并无影响屏障类型 - StoreStore:确保该内存屏障前的 Store 操作先于屏障后的所有 Store 操作。对于屏障前后的Load操作并无影响 - LoadStore:确保屏障指令之前的所有Load操作,先于屏障之后所有 Store 操作 - StoreLoad:确保屏障之前的所有内存访问操作(包括Store和Load)完成之后,才执行屏障之后的内存访问操作。全能型屏障,会屏蔽屏障前后所有指令的重排 在字节码层面上,变量添加 volatile 之后,读取和写入该变量都会加入内存屏障: **读取 volatile 变量时,在后边添加内存屏障,不允许之后的操作重排序到读操作之前** ```java volatile变量读操作 LoadLoad LoadStore ``` **写入 volatile 变量时,前后加入内存屏障,不允许写操作的前后操作重排序** ```java LoadStore StoreStore volatile变量写操作 StoreLoad ``` **volatile 的缺陷就是不能保证变量的原子性** 解决方案:可以通过加锁或者 `AtomicInteger`原子操作类来保证该变量操作时的原子性 ```java public static AtomicInteger count = new AtomicInteger(0); ```
X Tutup