volatile关键字

165 阅读3分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。

volatile关键字

Volatile是轻量级的sychronized,它通过保证共享变量在各个线程的“可见性”来实现。

它的轻体现在不会引起线程上下文的切换和调度

可见性:可见性指一个线程在修改变量的时候,另一个线程可以可以读取到修改后的值

Volatile的定义和实现原理

Volatile的定义

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致性更新,线程应该确保被排他锁单独获得这个变量。---《java编程语言规范》

Volatile的实现原理

我们可以从java代码转变的汇编代码中入手。

第一步:被volatile变量修饰的共享变量进行修改操作时,对应的汇编语言代码会多出第二行汇编代码,且前缀为Lock

第二步:Lock前缀的指令在多核处理器下会引发两件事

  1. 将当前处理器缓存行的数据写回到系统内存中去。(更新数据)
  2. 这个写回内存的操作会导致其他处理器对该变量存放的地址失效。(使用新地址)

PS:值得注意的是为了提高处理效率处理器不会和内存进行直接的通信,而是需要一个缓冲步骤。就是先把系统内存中的数据读到内部内存中去再进行操作,操作完后数据不一定会在何时写回到内存中去。而Volatile关键字修饰的变量在进行修改后JVM就会向处理器发送Lock前缀的指令,让处理器把数据写回到系统内存中去。

PS:在数据写回到系统内存中去后,在多处理器的情况下,其他处理器内部内存中数据依旧是旧数据,为了避免这个问题每个处理器会通过嗅探总线上的数据来对比自己的缓存值是否过期,如果过期了就把当前处理器的缓存行设置为无效状态,当处理器读取到该数据的时候就会把数据从系统内存读取到处理器缓存中去。

Volatile的使用优化

在JDK1.7中编程大师DougLea新增一个队列集合类LinkedTrasferQueue,通过追加字节来起到优化Volatile变量出队和入队的性能。

**方法便是追加字节到64字节 **

为什么要追加到64字节呢?

在如今的许多处理器中它们的内部高速缓冲行是64字节,且不支持部分填充缓存行。这就会导致如果队列的头节点和尾节点都没有64个字节,那么处理器会把它们存放到同一个高速缓存行中。而且缓存行是内存操作的最小单位。所以存放在同一个缓存行的头节点和尾节点其中如果有一个被修改就会锁住另外一个。所以把Volatile变量填充到64字节就避免了这个情况的发生,提高了队列在高并发编程中的效率。

什么时候不适合追加到64字节呢?

  1. 缓存行不是64字节宽的处理器。
  2. 共享变量不会被频繁的写。