1. 说说你对 volatile 的了解?
首先,给大家上一张图,咱们来一起看看:
如上图,这张图说的是 java 内存模型中,每个线程有本人的工作内存,同时还有一个共享的主内存。
举个例子,比如说有两个线程,他们的代码里都须要读取 data 这个变量的值,那么他们都会从主内存里加载 data 变量的值到本人的工作内存,而后才能够应用那个值。
好了,当初大家从图里看到,每个线程都把 data 这个变量的正本加载到了本人的工作内存里了,所以每个线程都能够读到 data = 0 这个值。
这样,在线程代码运行的过程中,对 data 的值都能够间接从工作内存里加载了,不须要再从主内存里加载了。
那问题来了,为啥肯定要让每个线程用一个工作内存来寄存变量的正本以供读取呢?我间接让线程每次都从主内存加载变量的值不行吗?
很简略!因为线程运行的代码对应的是一些指令,是由 CPU 执行的!然而 CPU 每次执行指令运算的时候,也就是执行咱们写的那一大坨代码的时候,要是每次须要一个变量的值,都从主内存加载,性能会比拟差!
所以说起初想了一个方法,就是线程有工作内存的概念,相似于一个高速的本地缓存。
这样一来,线程的代码在执行过程中,就能够间接从本人本地缓存里加载变量正本,不须要从主内存加载变量值,性能能够晋升很多!
然而大家思考一下,这样会有什么问题?
咱们来构想一下,如果说线程 1 批改了 data 变量的值为 1,而后将这个批改写入本人的本地工作内存。那么此时,线程 1 的工作内存里的 data 值为 1。
然而,主内存里的 data 值还是为 0!线程 2 的工作内存里的 data 值还是 0 啊?!
这可难堪了,那接下来,在线程 1 的代码运行过程中,他能够间接读到 data 最新的值是 1,然而线程 2 的代码运行过程中读到的 data 的值还是 0!
这就导致,线程 1 和线程 2 其实都是在操作一个变量 data,然而线程 1 批改了 data 变量的值之后,线程 2 是看不到的,始终都是看到本人本地工作内存中的一个旧的正本的值!
这就是所谓的 java 并发编程中的可见性问题:
多个线程并发读写一个共享变量的时候,有可能某个线程批改了变量的值,然而其余线程看不到!也就是对其余线程不可见!
2.volatile 的作用及背地的原理
那如果要解决这个问题怎么办呢?这时就轮到 volatile 闪亮退场了!你只有给 data 这个变量在定义的时候加一个 volatile,就间接能够完满的解决这个可见性的问题。
比方上面的这样的代码,在加了 volatile 之后,会有啥作用呢?
public class Helloworld{
private volatile int data =0;
// 线程 1 会读取和批改 data 变量值
// 线程 2 会读取 data 变量值
}
咱们这里说说最要害的几个作用是啥?
- 一旦 data 变量定义的时候后面加了 volatile 来润饰的话,那么线程 1 只有批改 data 变量的值,就会在批改完本人本地工作内存的 data 变量值之后,强制将这个 data 变量最新的值刷回主内存,必须让主内存里的 data 变量值立马变成最新的值!
整个过程,如下图所示: - 如果此时别的线程的工作内存中有这个 data 变量的本地缓存,也就是一个变量正本的话,那么会强制让其余线程的工作内存中的 data 变量缓存间接生效过期,不容许再次读取和应用了!
整个过程,如下图所示: - 如果线程 2 在代码运行过程中再次须要读取 data 变量的值,此时尝试从本地工作内存中读取,就会发现这个 data = 0 曾经过期了!此时,他就必须从新从主内存中加载 data 变量最新的值!那么不就能够读取到 data = 1 这个最新的值了!
整个过程,如下图所示:
bingo!好了,volatile 完满解决了 java 并发中可见性的问题!
对一个变量加了 volatile 关键字润饰之后,只有一个线程批改了这个变量的值,立马强制刷回主内存。
接着强制过期其余线程的本地工作内存中的缓存,最初其余线程读取变量值的时候,强制从新从主内存来加载最新的值!
这样就保障,任何一个线程批改了变量值,其余线程立马就能够看见了!这就是所谓的 volatile 保障了可见性的工作原理!
3. 总结 & 揭示
volatile 次要作用是保障可见性以及有序性。
volatile 是不能保障原子性的!
也就是说,volatile 次要解决的是一个线程批改变量值之后,其余线程立马能够读到最新的值,是解决这个问题的,也就是可见性!
然而如果是多个线程同时批改一个变量的值,那还是可能呈现多线程并发的平安问题,导致数据值批改错乱,volatile 是不负责解决这个问题的,也就是不负责解决原子性问题!
原子性问题,得依赖 synchronized、ReentrantLock 等加锁机制来解决。
以上文献均转自石杉老师架构课程!!!