一,先分析为什么用volatile,哪些时候用
是 java 内存模型中,每个线程有⾃⼰的⼯作内存,同时还有⼀个共享的主 内存,比如两个线程都要读data 这个共享变量的值,那么他们都会先从主内存读这两个变量的值
放到自己的工作内存,然后才去使用,所以每个线程都可以读到 data = 1 这个值
在线程代码运⾏的过程中,对 data 的值都可以直接从⼯作内存⾥加载了,不需要再从主内存⾥加载了
在线程代码运⾏的过程中,对 data 的值都可以直接从⼯作内存⾥加载了,不需要再从主 内存⾥加载了
假如说线程 1 修改了 data 变量的值为 2,然后将这个修改写⼊⾃⼰的本地⼯作内存。那么此时,线程 1 的⼯作内存⾥的 data 值为 2
然⽽,主内存⾥的 data 值还是为 1 线程 2 的⼯作内存⾥的 data 值还是1
在线程 1 的代码运⾏过程中,他可以直接读到 data 最新的值是 2,但 是线程 2 的代码运⾏过程中读到的 data 的值还是 1, 这就导致,线程 1 和线程 2 其实都是在操作⼀个变量 data,但是线程 1 修改了 data 变量的值之 后,线程 2 是看不到的,⼀直都是看到⾃⼰本地⼯作内存中的⼀个旧的副本的值! 这就是所谓的 java 并发编程中的可⻅性问题: 多个线程并发读写⼀个共享变量的时候,有可能某个线程修改了变量的值,但是其他线程看不 到!也就是对其他线程不可⻅!
二 volatile 的作⽤及背后的原理
⼀旦 data 变量定义的时候前⾯加了 volatile 来修饰的话,那么线程 1 只要修改 data 变量 的值,就会在修改完⾃⼰本地⼯作内存的 data 变量值之后,强制将这个 data 变量最新的值刷回主内存,必须让主内存⾥的 data 变量值⽴⻢变成最新的值!
如果此时别的线程的⼯作内存中有这个 data 变量的本地缓存,也就是⼀个变量副本的 话,那么会强制让其他线程的⼯作内存中的 data 变量缓存直接失效过期,不允许再次读取和使 ⽤了!
如果线程 2 在代码运⾏过程中再次需要读取 data 变量的值,此时尝试从本地⼯作内存中 读取,就会发现这个 data = 1 已经过期了!
此时,他就必须重新从主内存中加载 data 变量最新的值!那么不就可以读取到 data = 1 这个最 新的值了!
volatile 完美解决了 java 并发中可⻅性的问题! 对⼀个变量加了 volatile 关键字修饰之后,只要⼀个线程修改了这个变量的值,⽴⻢强制刷回 主内存。 接着强制过期其他线程的本地⼯作内存中的缓存,最后其他线程读取变量值的时候,强制重新 从主内存来加载最新的值! 这样就保证,任何⼀个线程修改了变量值,其他线程⽴⻢就可以看⻅了!这就是所谓的 volatile 保证了可⻅性的⼯作原理!
有序性涉及到较为复杂的指令重排、内存屏障等概念,本⽂没提及,但是 volatile 是不能保证 原⼦性的!
如果想要保证原子性得依赖 synchronized、ReentrantLock 等加锁机制来解决,但是像数据更新这种比较小的动作,用锁会严重影响性能,可以使用Atomic类来实现