深入学习 volatile 的特性

简介: 深入学习 volatile 的特性

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,阿里云专家博主,华为云云享专家✌

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

1688208248818.jpg

1、什么是 volatile

volatile是一个关键字,用于修饰变量。在Java中,volatile关键字用于确保多线程环境下的可见性和禁止指令重排序。

当一个变量被声明为volatile时,它的值的修改将立即对其他线程可见,而不会被线程的本地缓存所影响。这样可以确保多个线程对该变量的读写操作是一致的。

此外,volatile关键字还可以禁止编译器和处理器对指令的重排序优化,保证指令的执行顺序与程序的顺序一致。

需要注意的是,volatile关键字只能保证可见性和禁止指令重排序,并不能保证原子性。如果需要保证原子性操作,可以考虑使用synchronized关键字或者Atomic类。

2、volatile 能解决什么问题

volatile关键字主要用于解决多线程环境下的可见性和禁止指令重排序的问题。下面分别说明这两个问题:

  1. 可见性问题:在多线程环境下,当一个线程修改了一个共享变量的值时,其他线程可能无法立即看到这个修改,而是看到该变量的旧值。这是因为每个线程都有自己的本地缓存,对共享变量的修改可能先保存在本地缓存中,而不是立即写回主内存。这种情况下,其他线程就无法感知到这个修改,导致数据不一致的问题。使用volatile关键字修饰的变量,其修改操作将立即对其他线程可见,不会被本地缓存所影响,从而解决了可见性问题。

  2. 指令重排序问题:为了提高程序的执行效率,编译器和处理器在执行指令时可能会对其进行重排序优化。这种重排序优化在单线程环境下不会影响程序的结果,但在多线程环境下可能会导致线程之间的执行顺序出现问题,从而破坏了程序的正确性。使用volatile关键字修饰的变量,可以禁止编译器和处理器对指令的重排序优化,保证指令的执行顺序与程序的顺序一致,从而解决了指令重排序问题。

3、volatile 的底层原理

image.png

volatile的底层原理涉及到Java内存模型(Java Memory Model,简称JMM)和CPU的缓存机制。

在Java内存模型中,每个线程都有自己的工作内存,而共享变量则存储在主内存中。当一个线程要访问共享变量时,首先会将该变量从主内存中拷贝到自己的工作内存中,然后对该变量进行操作。操作完成后,再将变量的值写回到主内存中。

而对于被volatile修饰的变量,其读取和写入操作会有特殊的规则。当一个线程对volatile变量进行写操作时,会立即将修改后的值刷新到主内存中,而不是先在自己的工作内存中进行修改。这样,其他线程在读取该变量时就可以立即看到最新的值。同样地,当一个线程对volatile变量进行读操作时,会先将该变量的值从主内存中读取到自己的工作内存中,而不是从自己的本地缓存中读取。

底层实现上,volatile关键字通过使用内存屏障(Memory Barrier)来实现可见性和禁止指令重排序。内存屏障是一种硬件指令,它可以保证在执行屏障之前的所有读写操作都完成,并将结果刷新到主内存中,然后再执行屏障之后的读写操作。这样可以确保volatile变量的读写操作不会受到指令重排序的影响,从而保证了可见性和一致性。

4、指令重排(面试必问)

指令重排是指在编译器和处理器优化指令执行顺序的过程中,可能会改变原始程序中的指令顺序。这种优化是为了提高程序的执行效率和性能。然而,在多线程环境下,指令重排可能会导致线程之间的执行顺序出现问题,从而破坏了程序的正确性。

指令重排的原因是现代计算机体系结构中存在多级缓存和乱序执行等机制。处理器为了提高指令执行的效率,可能会对指令进行重排序,但要保证最终的执行结果与原始程序的顺序一致。

在多线程环境下,指令重排可能会引发一些问题,例如数据竞争和可见性问题。如果多个线程对共享变量进行读写操作,并且存在指令重排,那么就可能导致线程之间读取到的变量值不一致,从而产生错误的结果。

为了解决指令重排带来的问题,可以使用同步机制,如volatile关键字或synchronized关键字。这些机制可以禁止或限制指令重排,保证线程之间的执行顺序和结果的正确性。

在面试中,指令重排是一个常见的话题,面试官可能会询问你对指令重排的理解以及如何避免相关问题。了解指令重排的原因和解决方法,可以帮助你更好地理解多线程编程中的并发问题。

需要注意的是,volatile关键字只能保证可见性,不能保证原子性。如果需要保证原子性,可以考虑使用synchronized关键字或者Atomic类。 💕💕(记住这个重点,一般面试中面试官会问到)💕💕

5、可见性(面试必问)

volatile关键字可以确保多线程环境下共享变量的可见性。可见性是指当一个线程修改了某个共享变量的值时,其他线程能够立即看到这个修改后的值。

在没有使用volatile关键字的情况下,线程在读取共享变量时,可能会从自己的工作内存中读取变量的值,而不是从主内存中读取。这样就有可能导致一个线程对共享变量的修改对其他线程不可见,从而引发并发问题。

而使用volatile关键字修饰共享变量时,当一个线程修改了该变量的值,会立即将修改后的值刷新到主内存中,而其他线程在读取该变量时,会直接从主内存中读取最新的值,从而保证了可见性。

6、读写屏障(面试必问)

读写屏障(Memory Barrier)是一种硬件指令或者编译器插入的指令,用于确保在执行屏障之前的所有读写操作都完成,并将结果刷新到主内存中,然后再执行屏障之后的读写操作。读写屏障可以用来保证多线程环境下共享变量的可见性和一致性。

读写屏障有两种类型:读屏障和写屏障。

读屏障(Read Barrier)用于确保在读屏障之前的所有读操作都已完成,并将结果刷新到主内存中。读屏障可以防止指令重排序,保证后续的读操作不会读取到过期的值。

写屏障(Write Barrier)用于确保在写屏障之前的所有写操作都已完成,并将结果刷新到主内存中。写屏障可以防止指令重排序,保证后续的写操作不会被提前执行。

在多线程编程中,读写屏障的使用可以保证共享变量的可见性和一致性。当一个线程对共享变量进行写操作时,可以使用写屏障来确保该操作对其他线程可见。当一个线程对共享变量进行读操作时,可以使用读屏障来确保读取到的值是最新的。

7、volatile 的代码案例

使用 java 写一个案例:

package com.pany.camp.volatiles;

/**
 * @description: Volatile
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-07-01 18:57
 */
public class VolatileExample {
   
   

    private volatile boolean flag = false;

    public void setFlag(boolean value) {
   
   
        flag = value;
    }

    public boolean getFlag() {
   
   
        return flag;
    }

    public static void main(String[] args) {
   
   
        VolatileExample example = new VolatileExample();
        // 线程A修改flag的值为true
        Thread threadA = new Thread(() -> {
   
   
            example.setFlag(true);
        });
        // 线程B读取flag的值
        Thread threadB = new Thread(() -> {
   
   
            boolean flag = example.getFlag();
            System.out.println("Flag value: " + flag);
        });
        threadA.start();
        threadB.start();
    }
}

我们创建了一个名为VolatileExample的类,其中包含一个volatile修饰的boolean类型的变量flag。在主线程中,我们创建了两个线程,线程A负责修改flag的值为true,线程B负责读取flag的值并打印出来。

由于flag是使用volatile修饰的,线程B在读取flag的值时会直接从主内存中读取,而不会从自己的工作内存中读取。这样可以确保线程B看到的flag值是线程A修改后的最新值,而不是过期的值。

1687869804912.jpg

💕💕 本文由激流原创,原创不易,感谢支持
💕💕喜欢的话记得点赞收藏啊

1688203932038.jpg

目录
相关文章
C#学习相关系列之多线程---ConfigureAwait的用法
C#学习相关系列之多线程---ConfigureAwait的用法
510 0
C#学习相关系列之多线程---TaskCompletionSource用法(八)
C#学习相关系列之多线程---TaskCompletionSource用法(八)
758 0
|
安全 C++
c++11新特性——智能指针详解
c++11新特性——智能指针详解
|
10月前
|
机器学习/深度学习 存储 文字识别
阿里国际Ovis2系列模型开源:多模态大语言模型的新突破
阿里国际Ovis2系列模型开源:多模态大语言模型的新突破
470 0
|
3月前
|
搜索推荐 算法 大数据
基于python大数据的旅游景点可视化与推荐系统
本系统基于大数据与网络技术,构建个性化旅游推荐平台。通过收集用户偏好及行为数据,结合机器学习算法,提供精准的旅游目的地、住宿及交通推荐,旨在优化旅游信息传递,提升用户决策效率与旅行体验。
|
SQL 开发框架 .NET
C#一分钟浅谈:数据绑定与数据源控件
在Web开发中,数据绑定和数据源控件是实现动态网页的关键技术。本文从基础概念入手,详细讲解数据绑定的原理及其在ASP.NET中的应用,并介绍常见数据绑定方式:手动绑定和自动绑定。接着,文章重点介绍了ASP.NET中的数据源控件,如`SqlDataSource`、`ObjectDataSource`、`XmlDataSource`和`LinqDataSource`,并通过具体示例演示如何使用`SqlDataSource`和`GridView`进行数据绑定。最后,还列举了一些常见问题及其解决办法,帮助读者更好地理解和应用这些技术。
227 4
|
存储 缓存 算法
内存系列学习(四):Cache和Write Buffer一般性介绍
内存系列学习(四):Cache和Write Buffer一般性介绍
1061 0
|
编解码 监控 网络协议
如何优雅的实现一个靠谱的RTSP播放器?
二话不说,NO 图 NO BB(以大牛直播SDK播放海康摄像机RTSP H.265流为例):
514 0
|
机器学习/深度学习 人工智能 异构计算
阿里等发布基于3D的人物图片转视频模型Champ
【4月更文挑战第21天】阿里联合南京大学、复旦大学发布创新模型Champ,实现3D人物图片转视频的突破。Champ运用SMPL模型与潜在扩散框架,提升形状对齐和运动引导能力,生成高质量人物动画,尤其擅长捕捉姿势和形状变化。模型通过细节处理增强面部表情和手指动作等细节,但面部和手部建模仍有提升空间。研究团队已进行效率优化,推动实际应用。[项目地址](https://fudan-generative-vision.github.io/champ/#/) | [论文地址](https://arxiv.org/abs/2403.14781)
368 1
|
存储 开发者 Python
Python项目实战案例-批量下载网易云榜单音乐保存至本地
Python项目实战案例-批量下载网易云榜单音乐保存至本地
350 1

热门文章

最新文章