百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

关于 Java 关键字 volatile 的总结

yuyutoo 2025-05-02 22:16 4 浏览 0 评论


1 什么是 volatile

volatile 是 Java 的一个关键字,它提供了一种轻量级的同步机制。相比于重量级锁 synchronized,volatile 更为轻量级,因为它不会引起线程上下文的切换和调度。

2 volatile 的两个作用

  1. 可以禁止指令的重排序优化
  2. 提供多线程访问共享变量的内存可见性

3 禁止指令重排

3.1 什么是指令重排

指令重排序是 JVM 为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度,例如将多条指令并行执行或者是调整指令的执行顺序。但是在多线程的情况下,指令重排序可能会带来问题,例如程序执行的顺序可能会被调整。在加上 volatile 关键字之后可以有效解决这个问题。

下面我们举个例子:

double r = 2.1; //(1) 
double pi = 3.14;//(2) 
double area = pi*r*r;//(3)

在代码语句的顺序为 1->2->3,但实际上顺序无论是 1->2->3 还是 2->1->3 对结果并无影响,所以在编译时和运行时可以根据需要对1、2语句进行重排序。

重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。重排序需要遵守一定规则:
1 不会对存在数据依赖关系的操作进行重排序
2 重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变

3.2 指令重排带来的问题

我们来看看这个基于双重检验的单例模式:

public class Singleton3 {
 private static Singleton3 instance = null;

 private Singleton3() {}

 public static Singleton3 getInstance() {
 if (instance == null) {
 synchronized(Singleton3.class) {
 if (instance == null)
 instance = new Singleton3();// 非原子操作
 }
 }

 return instance;
 }
}

事实上,这个单例模式的实现方式是有问题的,问题在哪呢?问题在于instance = new Singleton3(); 并不是一个原子操作。

我们可以将其抽象成以下几条指令:

memory =allocate(); //1:分配对象的内存空间 
ctorInstance(memory); //2:初始化对象 
instance =memory; //3:设置instance指向刚分配的内存地址

可以看到,操作2依赖于操作1,但操作3并不依赖于操作2。所以 JVM 是可以针对它们进行指令的优化重排序的,经过重排序后如下:

memory =allocate(); //1:分配对象的内存空间 
instance =memory; //3:instance指向刚分配的内存地址,此时对象还未初始化
ctorInstance(memory); //2:初始化对象

指令重排之后,instance 指向分配好的内存放在了前面,而这段内存的初始化被排在了后面。在线程A执行这段赋值语句,在初始化分配对象之前就已经将其赋值给 instance 引用,恰好另一个线程进入方法判断 instance 引用不为 null,然后就将其返回使用,导致出错。

3.3 禁止指令重排的原理

volatile 关键字提供内存屏障的方式来防止指令被重排,编译器在生成字节码文件时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

内存屏障会确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

对于上面的基于双重检验的单例模式,我们只需对其稍作修改即可令其正确运行。我们已经知道,问题来自于指令重排,那么我们禁止指令重排即可,用 volatile 关键字修饰 instance 变量,使得 instance 在读、写操作前后都会插入内存屏障,避免重排序。完整代码如下:

public class Singleton3 {
 private static volatile Singleton3 instance = null;

 private Singleton3() {}

 public static Singleton3 getInstance() {
 if (instance == null) {
 synchronized(Singleton3.class) {
 if (instance == null)
 instance = new Singleton3();
 }
 }
 return instance;
 }
}

4 保证内存可见性

4.1 什么是保证内存可见性

Java 支持多个线程同时访问一个对象或者对象的成员变量,由于每个线程可以拥有这个变量的拷贝(虽然对象以及成员变量分配的内存是在共享内存中的,但是每个执行的线程还是可以拥有一份拷贝,这样做的目的是加速程序的执行,这是现代多核处理器的一个显著特性),所以程序在执行过程中,一个线程看到的变量并不一定是最新的。volatile 告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。

4.2 实现的具体细节

如果对声明了 volatile 的变量进行写操作,JVM 就会向处理器发送一条 Lock 前缀的指令,将这个变量所在缓存行的数据写回到系统内存。

但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。

具体的说,内存可见性也是通过内存屏障实现的,它会执行下面两个操作:

  1. 强制将对缓存的修改操作立即写入主存
  2. 如果是写操作,它会导致其他 CPU 中对应的缓存行无效

5 总结

  1. volatile 提供了一种轻量级的同步机制,在访问 volatile 变量时不会执行加锁操作,因此也就不会使执行线程阻塞
  2. volatile 只能确保可见性,而加锁机制既可以确保可见性又可以确保原子性
  3. volatile 屏蔽掉了 JVM 中必要的代码优化,所以在效率上比较低
  4. 相比 synchronized,虽然 volatile 更简单并且开销更低,但它的同步性较差,而且其使用也更容易出错

相关推荐

对volatile,synchronized,AQS的加锁解锁原理的一些理解

一、为什么要加锁,要实现同步多线程编程中,有可能会出现多个线程同时访问同一个共享、可变资源的情况,这个资源我们称之其为临界资源;这种资源可能是:对象、变量、文件等。...

注意,不能错过的CAS+volatile实现同步代码块

前言:最近看到有人说可以使用CAS+volatile实现同步代码块。心想,确实是可以实现的呀!因为AbstractQueuedSynchronizer(简称AQS)内部就是通过CAS+...

面试并发volatile关键字时,我们应该具备哪些谈资?

提前发现更多精彩内容,请访问https://dayarch.top/提前发现更多精彩内容,请访问https://dayarch.top/提前发现更多精彩内容,请访问https://dayarch...

无锁同步-JAVA之Volatile、Atomic和CAS

1、概要本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步。关于JAVA中的原子操作,我们很容易想到的是Volatile变量、java.util.concurrent....

C/C++面试题(二):std::atomic与volatile

volatile是C/C++中的一个关键字,用于告知编译器某个变量的值可能会在程序的控制之外被意外修改(例如被硬件、中断服务程序、多线程环境或其他外部代理)。为了防止编译器对代码进行某些可能破坏...

VOCs(Volatile Organic Compounds)挥发性有机化合物及测试方法

经常看到一些三防漆、涂料、油漆类产品的介绍中提到VOC、VOCs等概念,那么什么是VOC、VOCs和TVOC,VOCs主要包括哪些物质?VOCs的来源有哪些?VOCs的危害及国家标准是什么?一、V...

对volatile 及happen—before的理解

happen—before规则介绍Java...

这一篇我们来了解Synchronized、Volatile、Final关键字

题外话:蓝银王觉醒了!!--来自于一个斗罗大陆动漫爱好者(鹅,打钱!)湿兄这两天回家了,办了点大事,回来的时候我弟弟还舍不得我,哭着不愿意让我回京(我弟还是小学),我也心里很不舍,但是还是要回京奋斗...

关于 Java 关键字 volatile 的总结

1什么是volatilevolatile是Java的一个关键字,它提供了一种轻量级的同步机制。相比于重量级锁synchronized,volatile更为轻量级,因为它不会引起线程上下文...

大白话聊聊Java并发面试问题之volatile到底是什么?

用最简单的大白话,加上多张图给大家说一下,volatile到底是什么?...

为什么要有volatile关键字(volatile 关键字为什么不能保证原子性)

在嵌入式编程和多线程编程中,我们常会见到volatile关键字声明的变量。下面说一下volatile关键字的作用:1.保持变量内存可见简而言之就是用volatile声明的变量会告诉编译器和处理器,这个...

Java的volatile到底怎么理解?(java volatitle)

我们都知道,在Java中有很多的关键字,比如synchronize比如volatile,这些都是一些比较关键的,还有final,今天我们就来聊一下这个volatile因为这个vo...

Java多线程编程中的volatile关键字:解密神秘的共享内存

Java多线程编程中的volatile关键字:解密神秘的共享内存在Java多线程编程的世界里,volatile关键字就像一位低调却至关重要的守护者。它默默无闻地站岗放哨,确保多个线程之间能够正确地共享...

你了解volatile关键字的作用吗?(关键字volatile有什么含意?并举出三个不同的例子?)

【死记硬背】volatile关键字主要用于保持内存的变量可见性和禁止重排序。变量可见性:当一个线程改变了变量的值,那么新的值对于其他线程也是可以立即获取到的。禁止重排序:...

谈谈你对volatile 关键字作用和原理的理解

一位6年工作经验的小伙伴,在某里二面的时候被问到“volatile”关键字。然后,就没有然后了…同样,还有一位4年的小伙伴,去某团面试也被问到“volatile关键字“。然后,也没有然后了…...

取消回复欢迎 发表评论: