掉了两根头发,可算是把volatile整明白了

Java基础:volatile详解 Java基础:volatile1、volatile保证可见性1.1、JMM模型的引入1.2、volatile保证可见性的代码验证1.2.1、无可见性代码验证1.2.1、volatile保证可见性验证2、volatile不保证原子性 问:请谈谈你对volatile的理解? 答:volatile是Java虚拟机提供的轻量级的同步机制,它有3个特性: 1)保证可见性 2)不保证原子性 3)禁止指令重排 刚学完java基础,如果有人问你什么是volatile?它有什么作用的话,相信一定非常懵逼… 可能看了答案,也完 阅读详情

volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但对于为什么它只能保证可见性,不保证原子性,它又是如何禁用指令重排的,还有很多同学没彻底理解

相信我,坚持看完这篇文章,你将牢牢掌握一个Java核心知识点

先说它的两个作用:

保证变量在内存中对线程的可见性
禁用指令重排
每个字都认识,凑在一起就麻了

这两个作用通常很不容易被我们Java开发人员正确、完整地理解,以至于许多同学不能正确地使用volatile

public class VolatileTest {
    private static volatile int count = 0;

    private static void increase() {
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    increase();
                }
            }).start();
        }
        // 所有线程累加完成后输出
        while (Thread.activeCount() > 2) Thread.yield();
        System.out.println(count);
    }
}

代码很好理解,开了十个线程对同一个共享变量count做累加,每个线程累加1w次

count我们已经用volatile修饰,已经保证了count对十个线程在内存中的可见性,按理说十个线程执行完毕count的值应该10w

然鹅,运行多次,结果都远小于期望值

是哪个环节出了问题?

你肯定听过一句话:volatile只保证可见性,不保证原子性

这句话就是答案,但是依旧很多人没搞懂其中的奥秘

说来话长我长话短说,简单来讲就是 count++这个操作不是原子的,它是分三步进行

1.从内存读取 count 的值
2.执行 count + 1
3.将 count 的新值写回
要彻底搞懂这个问题,我们得从字节码入手

下面是increase方法编译后的字节码

看不懂没关系,我们一行一行来看:

1.GETSTATIC:读取 count 的当前值
2.ICONST_1:将常量 1 加载到栈顶
3.IADD:执行+1
4.PUTSTATIC:写入count最新值

ICONST_1和IADD其实就是真正的++操作

关键点来了,volatile只能保证线程在GETSTATIC这一步拿到的值是最新的,但当该线程执行到下面几行指令时,这期间可能就有其它线程把count的值修改了,最终导致旧值把真正的新值覆盖

懂我意思吗

所以,并发编程中,只靠volatile修饰共享变量是不可靠的,最终还是要通过对关键方法加锁来保证线程安全

就如上面的demo,稍加修改就能实现真正的线程安全

最简单的,给increase方法加个synchronized (synchronized怎么实现线程安全的我就不啰嗦了,我以前讲过 synchronized底层实现原理)

    private synchronized static void increase() {
        ++count;
    }

run几下

这不就妥了嘛

到现在,对于以下两点你应该有了新的认知

volatile保证变量在内存中对线程的可见性
volatile只保证可见性,不保证原子性
关于指令重排
并发编程中,cpu自身和虚拟机为了提高执行效率,都会采用指令重排(在保证不影响结果的前提下,将某些代码乱序执行)

关于cpu:为了从分利用cpu,实际执行指令时会做优化;
关于虚拟机:在HotSpot vm中,为了提升执行效率,JIT(即时编译)模式也会做指令优化
指令重排在大部分场景下确实能提升执行效率,但有些场景对代码执行顺序是强依赖的,此时我们需要禁用指令重排,如下面这个场景

伪代码取自《深入理解Java虚拟机》:

其描述的场景是开发中常见配置读取过程,只是我们在处理配置文件时一般不会出现并发,所以没有察觉这会有问题。
试想一下,如果定义initialized变量时没有使用volatile修饰,就可能会由于指令重排序的优化,导致位于线程A中最后一条代码“initialized=true”被提前执行(这里虽然使用Java作为伪代码,但所指的重排序优化是机器级的优化操作,提前执行是指这条语句对应的汇编代码被提前执行),这样在线程B中使用配置信息的代码就可能出现错误,而volatile通过禁止指令重排则可以避免此类情况发生

禁用指令重排只需要将变量声明为volatile,是不是很神奇

我们来看看volatile是如何实现禁用指令重排的

也借用《深入理解Java虚拟机》的一个例子吧,比较好理解

这是个单例模式的实现,下面是它的部分字节码,红框中 mov%eax,0x150(%esi) 是对instance赋值

可以看到,在赋值后,还执行了 lock addl$0x0,(%esp) 指令,关键点就在这儿,这行指令相当于此处设置了个 内存屏障 ,有了内存屏障后,cpu或虚拟机在指令重排时就不能把内存屏障后面的指令提前到内存屏障前面,好好捋一下这段话

Java volatile详解 一 简介 volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。 二 并发编程的3个基本概念 1.原子性 定义: 即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 阅读详情

相关推荐

C语言位运算与逻辑运算深度解析:从底层原理到实战应用

在计算机编程中,位运算和逻辑运算是处理二进制数据的核心操作。位运算直接操作数据的二进制位,包括按位与(&)、或(|)、异或(^)、取反(~)和移位(<<, >>),其原理基于布尔代数,能实现高效的位级操作。逻辑运算(&&, ||, !)则关注表达式的真值,具有短路求值特性。这些运算的技术价值在于它们提供了直接与硬件交互的能力,是底层编程、性能优化和内存管理的关键。在嵌入式系统开发中,位运算用于配置硬件寄存器;在算法设计中,可用于状态压缩和快速计算;在系统编程中,逻辑运算的短路特性保障了代码安全性。本文聚焦C

weixin_33816300的博客 391

Java中的volatile

​ volatile是Java提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色。同synchronized相比(synchronized通常称为重量级锁),volatile更轻量级,相比使用synchronized所带来的庞大开销,倘若能恰当的合理的使用volatile,自然是美事一桩。为了能比较清晰彻底的理解volatile,我们一步一步来分析。首先来看看如下代码 上面这个例子,模拟在多线程环境里,t1线程对flag共享变量修改的值能否被t2可见,即是否输出 “-----flag被设置为

HGW的博客 2万+

USB OTG控制器核心寄存器实战解析:FRINDEX、PERIODICLISTBASE与OTGSC

USB(通用串行总线)是现代嵌入式系统中不可或缺的通信接口,其核心在于控制器对数据传输的精确调度与管理。USB OTG(On-The-Go)技术则扩展了这一能力,允许设备动态切换主机(Host)与设备(Device)角色,其实现依赖于控制器内部一套精密的状态机与寄存器组。理解这些寄存器的原理,是进行底层驱动开发与调试的关键。从概念上讲,USB通信基于严格的时序框架,例如以1毫秒为单位的帧(Frame)和125微秒的微帧(Microframe),这需要控制器具备精确的时间基准和任务调度能力。其技术价值在于,通

weixin_34302798的博客 479

Java 之 volatile 详解

volatile 可以看做是轻量级的 synchronized,它只保证了共享变量的可见性,是Java虚拟机提供的轻量级的同步机制。在线程 A 修改被 volatile 修饰的共享变量之后,线程 B 能够读取到正确的值。Java 在多线程中操作共享变量的过程中,会存在指令重排序与共享变量工作内存缓存的问题。volatile 一共有三大特性,保证可见性不保证原子性禁止指令重排。

流华追梦的专栏 8525

C# Lazy<T>深度解析:从延迟加载到人生操作系统

Lazy<T>是.NET中实现延迟初始化的核心机制,其本质是一种运行时契约而非语法糖,通过封装值创建逻辑、控制执行时机与管理线程安全状态,显著提升系统资源利用率和响应性能。它在Web服务、桌面应用及微服务配置加载等场景中广泛用于按需实例化高开销对象,避免启动阻塞与内存浪费。深入理解其三种线程安全模式(ExecutionAndPublication、PublicationOnly、None)及状态机行为(如IsValueCreated的语义陷阱),对构建健壮、可观测、可调试的生产级系统至关重要。本文结合C#底

xiang_fu的博客 364

Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂)

本文从Java的内存模型和线程的工作原理出发,对volatile关键字进行介绍。帮助读者深入理解volatile的工作机制。同时,还通过简单易懂的示例讲解了如何正确地使用volatile关键字实现线程安全,避免常见的多线程安全问题。

夏日清风 25万+

三年Java面试被问volatile底层,我用老汪的“交通警察”类比一次讲清

本文采用“模拟面试+技术深潜”的叙事形式,讲述了程序员小强在Java多线程面试中连续受挫,后在架构师老汪点拨下理解底层原理的故事。

程序员小星星的博客 235

代码复现——SymAgent:一种用于知识图谱复杂推理的神经符号自学习Agent框架

根据《SymAgent: A Neural-Symbolic Self-Learning Agent Framework for Complex Reasoning over Knowledge Graphs》(《SymAgent:一种用于知识图谱复杂推理的神经符号自学习Agent框架》)论文思路,简单复现代码,以实现SymAgent中的Agent-Planner和Agent-Executor模块。Agent-Planner利用大语言模型的归纳推理能力从知识图谱中提取符号规则,指导高效的问题分解。

qq_60040306的博客 1607

【020】❓ 问: Java中的内存模型以及其对并发编程的影响

Java内存模型(JMM)是并发编程的核心规则,定义了线程如何操作共享变量。其核心问题包括:可见性(线程修改共享变量后其他线程能否立即看到)、原子性(操作是否不可分割)和顺序性(指令执行顺序的保证)。 库存超卖问题的根源是可见性缺陷——线程修改变量后未及时同步到主内存,导致其他线程读取旧值。通过volatile关键字可强制变量修改立即刷新到主内存,解决可见性问题。但volatile无法保证原子性,需结合锁或原子类实现完整线程安全。理解JMM规则是避免并发Bug的关键。

编程技术圈 740

别再为数码管引脚发愁了!用STM32的普通IO口驱动TM1650数码管模块(附完整模拟I2C代码)

本文详细介绍了如何利用STM32的普通IO口通过模拟I2C协议驱动TM1650数码管模块,提供了完整的硬件连接方案和软件实现代码。通过模拟I2C技术,开发者可以灵活使用任意GPIO引脚控制多位数码管显示,解决了传统驱动方式占用大量IO资源的问题。文章包含TM1650寄存器解析、亮度控制等实用功能实现,并分享了常见问题排查与性能优化技巧。

weixin_30764771的博客 659

爬虫实战:拆解WASM加密参数,破局2026电商动态接口反爬(实测某头部电商可用)

sign和wasm_token,而且每次请求这两个参数都在动态变化。用常规的参数拼接、MD5加密尝试还原,结果全是403;反编译前端JS,发现核心加密逻辑被打包成了.wasm,把关键参数加密、签名逻辑藏在二进制模块里,让爬虫无法直接逆向JS拿到加密规则。前后折腾了5天,从WASM文件解析、JS与WASM交互逻辑拆解,到本地还原加密参数、实战验证,踩了无数坑(比如WASM版本不兼容、内存数据读取失败、加密依赖环境缺失),最终成功还原了加密流程,稳定爬取商品详情、价格、销量等核心数据,连续运行72小时无封禁。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1913

C语言循环语句深度解析:for/while/do-while原理、优化与实战避坑指南

循环是编程中实现重复执行的核心控制结构,其本质是通过条件判断和状态更新来控制代码块的迭代执行。从原理上看,循环通过初始化、条件检查、循环体执行和状态更新四个步骤,将重复性劳动转化为自动化流程,极大提升了代码的抽象能力和执行效率。在技术价值层面,循环不仅是算法实现(如排序、查找)和数据遍历(数组、字符串处理)的基础,更是构建复杂逻辑(如嵌套循环处理多维数据)和系统功能(如文件I/O、硬件轮询)的关键。在应用场景上,无论是嵌入式开发中的硬件状态等待,还是数据处理中的批量操作,循环都扮演着不可或缺的角色。本文聚焦

weixin_32306957的博客 214

Android Framework WMS全面概述和知识要点

WindowManagerService 类是整个 WMS 的核心大脑,处于系统服务层,负责协调和管理所有与窗口相关的操作。它在 Android 系统架构中起到承上启下的关键作用,向上对接应用层的窗口请求,向下与 SurfaceFlinger 等底层组件交互,确保窗口能够正确地在屏幕上显示和管理。

大模型大数据攻城狮的专栏 1795

Tiva™ TM4C123深度睡眠与时钟门控实战:精细功耗管理指南

在嵌入式系统开发中,低功耗设计是提升设备续航能力的关键技术。其核心原理在于通过动态管理微控制器内部时钟与电源状态,有效降低系统运行时的能量消耗。时钟门控技术通过硬件级控制,精准关闭闲置外设模块的时钟信号,从而消除其动态功耗,是实现精细功耗管理的底层基础。这项技术的工程价值在于,它允许开发者在保证功能实时性的前提下,将系统功耗压榨至接近芯片理论极限,尤其适用于电池供电的物联网终端、便携式医疗设备等对能效敏感的场景。本文以Tiva™ TM4C123微控制器为例,深入剖析其深度睡眠模式下的时钟门控机制,并聚焦于P

weixin_30256901的博客 301

浪涌抗扰度部分解读

8.3 测试仪器: 8.3.1浪涌发生器(对于连接到对称通信线的接口采用10/700us的组合波发生器;对于其他情况采用1.2/50us(8/20us)组合波发生器,输出极性正负可调、移相范围0-360°可调,最大重复率至少每分钟1次,开路输出电压0.5-4kV,电源线的话有效效输出阻抗为2Ω,通信线的话有效输出阻抗为40Ω) 8.3.2 耦合/去耦网络 8.3.3 参考接地平板 8...

喵叫兽的博客 2505

Android 12.0 系统systemui状态栏下拉左滑显示通知栏右滑显示控制中心模块的流程分析

在android12.0的系统rom定制化开发中,在系统原生systemui进行自定义下拉状态栏布局的定制的时候,需要在systemui下拉状态栏下滑的时候,根据下滑坐标来 判断当前是滑出通知栏还是滑出控制中心模块,所以就需要根据屏幕宽度,来区分x坐标值为多少是左滑出通知栏或者右滑出控制中心模块,接下来分析下 状态栏下滑坐标等相关功能代码分析

安卓兼职framework和app工程师的博客 2769

PN7462 USB设备控制器:寄存器配置与中断处理实战指南

USB(通用串行总线)作为嵌入式系统与主机通信的核心接口,其底层驱动开发关键在于理解硬件控制器的工作原理。控制器通过寄存器映射与固件交互,采用基于描述符列表和数据缓冲区的架构管理端点通信。其核心价值在于通过硬件自动处理繁琐的物理层与链路层协议(如CRC校验、包应答),并通过中断机制高效通知固件处理结果,从而解放CPU资源,提升系统实时性与吞吐率。这种机制广泛应用于需要可靠、高速数据传输的场景,如大容量存储设备、人机接口设备(HID)以及工业数据采集。本文以NXP PN7462微控制器的USB全速设备控制器为

weixin_34248023的博客 345

从权限对象到业务角色:把 SAP Authorization Object 讲清楚,并用 ABAP 写对一次 AUTHORITY-CHECK

摘要:SAP权限管理的核心在于Authorization Object(授权对象),它通过组合最多10个授权字段来精确控制用户访问权限。关键要点包括:1) 授权对象与ACTVT字段(如01创建/02修改/03显示)的配合使用;2) ABAP中AUTHORITY-CHECK语句的四种常见返回码(0通过/4字段不匹配/12无权限/40无效用户);3) 系统自动执行的S_TCODE事务码检查;4) 自定义授权对象时应遵循"最少且足够"原则,优先复用标准对象。在RAP和OData场景中,权限检查

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 272

java基础—Volatile关键字详解

volatile详解—并发编程3大特性、volatile从CPU层面和JMM层面如何保证可见性、有序性,为什么不能保证原子性、happens-before讲解以及案例演示

江哈哈的博客 4715

Vue - 详细完美解决 ElementUI MessageBox “多次“ 弹框问题,让其无论执行多少次都只弹出来一次(常见于请求拦截器多次触发弹框,导致弹出来很多个)详细示例代码,保证完美解决!

vue如何防止ElementUIMessageBox多次弹框,vueelement弹框弹出来很多次怎么办,elementvueMessageBox解决执行多次后出现多次弹框的问题,elementUI如何阻止MessageBox多次弹出来,vue+element解决多个请求同时报错,弹出多个消息框message,饿了么组件库MessageBox弹框频繁弹出解决方法,vue调用MessageBox出现多次弹框,vueelementMessageBox怎么只弹出一次,vue之axios请求

🏆 前端领域优质创作者 8196

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

JAVA多线程之volatile关键字

volatile关键字如何保证可见性和有序性,介绍读屏障和写屏障,介绍了DCL问题及其解决方式

ifhuke的博客 1861

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Java中的volatile关键字详解

volatile关键字提供了一种比synchronized更轻量级的线程安全机制,但它只能保证可见性和有序性,不能保证原子性。正确使用volatile可以避免不必要的同步开销,但在需要保证复合操作原子性或互斥性的场景下,仍然需要使用synchronized或其他并发工具。理解volatile的关键在于理解Java内存模型(JMM)以及happens-before原则,它在多线程编程中是一个非常有用的工具,但需要谨慎使用。

weixin_43680337的博客 1400

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

上一篇: 怎样解析java中的封装
下一篇: 云原生Java框架有哪些呢
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值