X Tutup
Skip to content

Latest commit

 

History

History
80 lines (45 loc) · 3.11 KB

File metadata and controls

80 lines (45 loc) · 3.11 KB

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 变量时,在后边添加内存屏障,不允许之后的操作重排序到读操作之前

volatile变量读操作
LoadLoad 
LoadStore

写入 volatile 变量时,前后加入内存屏障,不允许写操作的前后操作重排序

LoadStore
StoreStore 
volatile变量写操作
StoreLoad

volatile 的缺陷就是不能保证变量的原子性

解决方案:可以通过加锁或者 AtomicInteger原子操作类来保证该变量操作时的原子性

public static AtomicInteger count = new AtomicInteger(0);
X Tutup