Java并发—volatile关键字

news2025/1/17 3:49:15

在这篇文章Java并发—Java内存模型以及线程安全-CSDN博客多次提及volatile关键字,这是一个非常重要的概念,主要用于多线程编程中,它确保了变量的可见性和禁止指令重排序,但不保证原子性,下面详细解释volatile关键字的作用和特性:

1、volatile关键字的两层语义

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

1)保证可见性

保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。


上篇文中有讲一个例子:🌰线程A和线程B从主内存读取和修改x=1的过程

419f7d591bdf461fa9f107e3838ff3e4.png

由于线程A对变量的修改x=2未立即对线程B可见,造成了线程安全问题,为了确保线程间的即时通信和数据一致性,使用 volatile 关键字是必要的

如果在用volatile修饰变量x后,都会从主内存中读取最新的最新的值,而不是从线程自己的工作内存中读取可能过期的版本

线程A和线程B要进行通信的话,必须要进行以下几个步骤:

  1. 初始化:x = 1,存储在主内存。

  2. 线程A读取:A从主内存读取x,复制值1到A的工作内存。

  3. 线程B读取:B从主内存读取x,复制值1到B的工作内存。

  4. 线程A修改:A在工作内存中修改x至2

  5. 线程A写回:A将工作内存中的x值2写回主内存。

  6. 线程B重新读取:B从主内存读取最新的x值2,保证了数据的可见性。

这个过程展示了JMM如何确保多线程环境下的数据一致性


因此:volatile写-读的内存语义

  • 当写一个volatile变量时,JMM会把该线程对应的本地内存中的变量值刷新到主内存。

  • 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程将从主内存中读取共享变量

2)禁止进行指令重排序

volatile关键字禁止指令重排序有两层意思:

  • 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;

  • 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行


🌰例子:以下面的代码来讲述使用和未使用volatile关键字会出现什么杨的情况来理解指令重排

 🔴未使用 volatile 关键字

class Example {
    int a = 0;
    boolean ready = false;

    public void writerThread() {
        a = 5;           // 语句1
        ready = true;    // 语句2
    }

    public void readerThread() {
        while (!ready) { // 语句3
            Thread.yield();
        }
        System.out.println(a); // 语句4
    }
}

编译器和处理器可能会为了优化性能而对指令进行重排序。在这种情况下,语句1和语句2之间没有数据依赖关系,所以它们可能被重排序。

例如:处理器可能会先执行语句2再执行语句1

可能的执行顺序

        写线程:执行语句2,然后执行语句1。

        读线程:执行语句3,检查 ready 是否为 true。如果 ready 已经被设置为 true(即使 a 还没有被设置为5),读线程将进入语句4并打印 a 的值,此时 a 可能还是默认的0。

结果

由于指令重排序,读线程可能在 a 被写线程修改之前就读取了 a 的值,导致输出结果为0,而不是预期的5。这就是指令重排序可能导致的线程安全问题

 🔴使用 volatile 关键字

class Example {
    volatile int a = 0;
    volatile boolean ready = false;

    public void writerThread() {
        a = 5;           // 语句1
        ready = true;    // 语句2
    }

    public void readerThread() {
        while (!ready) { // 语句3
            Thread.yield();
        }
        System.out.println(a); // 语句4
    }
}

指令重排序限制

volatile 关键字会阻止编译器和处理器对与 volatile 变量相关的指令进行重排序。这意味着语句1和语句2之间的执行顺序将被保留,确保了写线程先修改 a 的值,然后再设置 ready 为 true

结果

由于 volatile 的内存屏障效果,读线程在检查 ready 是否为 true 并进入语句4之前,将看到写线程对 a 的最新修改,从而避免了指令重排序引起的线程安全问题


总结来说,volatile 关键字通过提供内存屏障来限制指令重排序,确保了变量的可见性和一定程度上的有序性,从而帮助解决多线程环境下的指令重排序问题

2、 volatile关键字的使用场景

  • 适用场景:volatile适用于那些被多个线程访问但并不涉及复合操作(例如递增操作)的变量。典型的使用场景包括状态标志、控制变量等。

  • 不适用场景:不要将volatile用于需要原子性操作的场景,因为volatile并不能保证原子性。对于需要原子性操作的场景,应该使用锁或者Atomic原子类

实际开发中,几乎看不到volatile的使用,因为volatile只能保证可见性,并不能保证原子性,就需要结合CAS(Compare and Swap)

其实在Java中,java.util.concurrent.atomic包提供了一组原子类,比如AtomicIntegerAtomicLongAtomicBoolean等,它们提供了一种无锁的线程安全机制,以确保对变量的操作是原子性的。

当谈到Atomic原子类的实现原理时,CAS操作是其中的关键。CAS是一种乐观锁技术,它涉及比较内存中的值和预期值,如果相等,则使用新值替换内存中的值。在Java中,CAS是通过Unsafe类实现的,它是一种硬件级别的原子性操作

但是,CAS操作本身无法解决线程可见性的问题,这就是volatile关键字的作用。volatile关键字可以确保变量的写操作立即可见于其他线程,从而解决了线程之间的可见性问题。因此,Atomic原子类是结合了CAS和volatile关键字来实现线程安全

一般情况下都是直接使用的Atomic原子类来保证线程安全的情况,并不会去直接使用volatile关键字

CAS的原理在下篇文章再讲吧……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1967890.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

未来3-5年,哪些工作会被AI取代

一篇由高盛经济学家约瑟夫布里格斯 (Joseph Briggs)和德维西科德纳尼 (Devesh Kodnani)撰写的报告指出,全球预计将有3亿个工作岗位被生成式AI取代。 报告称:“最近出现的生成式人工智能将降低劳动力成本和…

​宁德时代:续航还剩多少?

车企价格战打到供应商,连续增利不增收。 今天我们看宁德时代的增长电池续航还剩多少? 巨头长成,就要面临增长瓶颈。“宁王”24年中报公布,业绩喜忧参半。二季度营收869.96亿,同比下滑13.18%, 已经是宁德时…

冠军之选:奥运冠军青睐的游泳耳机款式大公开

在最新一届的夏季奥林匹克运动会中,泳池边的激烈竞争再次点燃了全球观众的热情。游泳运动员们,以惊人的速度和毅力,一次又一次地刷新纪录,向世人展示了人类极限的无限可能。而在这些运动员备战的过程中,有一个细节或许…

吴恩达老师机器学习-ex5

有借鉴网上部分博客 首先,我先使用该数据集,通过线性回归的方法,做了一个预测问题 import numpy as np import scipy.io as sio import matplotlib.pyplot as plt from scipy.optimize import minimize#读取数据 path "./ex5data1.ma…

Spine 核心功能入门

核心功能入门 本文主旨是整理我在入手学习 spine 时的流程,以及对于基本功能的理解和常规 2D 动画实现的思路。 意在整理出一个简要的入门 spine 的流程,以及对于一些高阶功能的应用的思考。 本文基于 https://zh.esotericsoftware.com/ 官网教程进行思…

2024.8.1 作业

使用两个线程完成两个文件的拷贝&#xff0c;分支线程1拷贝前一半&#xff0c;分支线程2拷贝后一半&#xff0c;主线程回收两个分支线程的资源 #include <myhead.h>struct Buf {const char *file1;const char *file2;int start;int size; };int get_len(const char *arr…

从线段中搜寻提取闭合轮廓(二)

接上篇文章从线段中搜寻闭合轮廓_多线段搜索区域集合 快速-CSDN博客 1. 前言 调试了上篇文章中参考代码修了一些问题&#xff0c;优化了显示&#xff0c;但是由于算法逻辑存在一些问题&#xff0c;有很多不必要的性能损耗&#xff0c;且逻辑不是最优的&#xff0c;于是博主找…

FPGA开发——蜂鸣器实现音乐播放器的设计

一、概述 我们在进行蜂鸣器的学习的时候&#xff0c;总会在想既然蜂鸣器能够发出声音&#xff0c;那么它能够播放音乐吗&#xff0c;今天这篇我们文章我们就一起来学习怎样使用使用蜂鸣器来播放音乐&#xff0c;也就是怎样成为一个音乐播放器。 1、蜂鸣器的类型 在设计的时候…

玩机进阶教程-----手机恢复出厂 误删除照片视频 误刷机后 几种数据恢复操作步骤解析【一】

手机中存储有众多的照片 视频 文件或者电话本这类的数据,虽然目前很多机型都有云存储。可以随时同步手机的存储数据。但万一云存储没有开启同步或者密码忘记。或者恢复出厂等等原因造成以上的数据丢失。或者手机系统问题导致的不开机但需要其中的数据等等。那么如何简单快速的…

【项目日记(五)】梦幻笔耕-测试报告

❣博主主页: 33的博客❣ ▶️文章专栏分类:项目日记◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多项目内容 目录 1.项目背景2.测试环境3.测试计划3.1功能测试3.2自动化测试 1.项目背景 个人博客系统…

剪画小程序:自媒体创业者的准备-文案!

作为一个刚刚踏入自媒体领域的新人&#xff0c;我满怀着激情和憧憬&#xff0c;渴望通过视频分享自己的见解和生活点滴。然而&#xff0c;视频文案的创作却成了我面前难以逾越的高山。 在构思旅行视频时&#xff0c;面对美丽的风景和丰富的经历&#xff0c;我却无法用恰当的文字…

嵌入式学习Day19---Linux软件编程

目录 一、标准I/O 1.1.fseek 1.偏移量 2.实例 ​编辑 1.2.ftell 2.实例 ​编辑 二、文件I/O 2.1.打开文件 1.open 2.2.实例 2.2.读写文件 1.write 实例 ​编辑 2.read 实例 2.3.关闭文件 1.close 2.3.lseek 实例 三、标准I/O与文件I/O的区别 3.1.区别 四、其…

2024年有哪些开放式耳机值得入手?精选五大高分品牌

近几年兴起的开放式蓝牙耳机&#xff0c;具有佩戴舒适稳固、不影响使用者判断外界环境等优点&#xff0c;十分适合在户外环境下使用&#xff0c;因此受到了众多健身人士的喜爱。那么该如何挑选到一款适合自己的开放式耳机呢&#xff1f;2024年有哪些开放式耳机值得入手&#xf…

【架构】应用保护

这篇文章总结一下应用保护的手段。如今说到应用保护&#xff0c;更多的会想到阿里的sentinel&#xff0c;手段丰富&#xff0c;应用简单。sentinel的限流、降级、熔断&#xff0c;可以自己去试一下&#xff0c;sentinel主要通过配置实现功能&#xff0c;不难。sentinel的简介放…

Qt如何在工程中使用dll库

DEMO&#xff1a;Test &#xff1b;工程与dll皆为qmake编译&#xff1b; 所需文件&#xff1a;A.dll、A.lib、A.h、A_global.h&#xff1b;B.dll。其中A.dll 依赖 B.dll 1. 环境配置 &#xff08;1&#xff09;确认制作dll的Qt版本和当前工程版本是否一致(Qt6中的一些函数Qt…

SLAM中的概率基础(知识回顾)

今天有些知识点忘记了&#xff0c;特地过来回顾一下&#xff0c;于是就做了这些笔记。 为了方便能够在手机上更直观的看笔记&#xff0c;写下这篇文章&#xff0c;有错误请各位大佬指出。 一、 概率基础概念 1.1. 概率密度函数&#xff08;PDF&#xff09; 概率密度函数用于…

第十九次(安装nginx代理tomcat)

回顾 1.安装nodejs---jdk一样你的软件运行环境 yum -y list install|grep epel $? yum -y install nodejs #版本号 node -v 2.下载对应的nodejs软件npm yum -y install npm npm -v npm set config ...淘宝镜像 3.安装vue/cli command line interface 命令行接口 npm ins…

推荐4个国内可用的AI软件,用上以后都能早点下班

随着技术的发展&#xff0c;越来越多的AI软件出现在人们的视野&#xff0c;应用的领域更多&#xff0c;能力提升也更快&#xff0c;用在工作上能帮不少忙。下面就给大家分享4个国内可以直接使用的AI软件&#xff0c;希望大家啊用上以后都能早点下班~ 1.Kimi 一键直达>>h…

韶音开放式耳机怎么样?韶音、西圣、QCY热门款实测横评

开放式耳机是目前最火爆的的耳机市场细分赛道&#xff0c;开放式耳机的优点包括健康卫生&#xff0c;佩戴舒适性高&#xff0c;方便我们接收外部环境音等等&#xff0c;以上这些优势使得开放式耳机特别适配户外运动场景&#xff0c;在工作、日常生活等场景下使用也是绰绰有余。…

Day81 代码随想录打卡|贪心算法篇---跳跃游戏 II

题目&#xff08;leecode T45&#xff09;&#xff1a;给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j…