java volatile关键字

详解Java并发编程volatile关键字 文章摘要 volatile是轻量级的synchronized,通过无锁方式保证变量可见性。文章通过代码示例展示了普通变量在多线程环境下的不可见性问题,并说明64位变量(如double/long)存在非原子性风险。使用volatile修饰后,线程能及时感知变量变化。 从JMM模型角度,volatile遵循happens-before原则,确保写操作对后续读操作可见。汇编代码显示volatile变量操作后会插入lock指令,强制刷新处理器缓存。硬件层面基于MESI协议,通过总线嗅探机制使其他处理器缓存失效,保证 阅读详情

java volatile关键字

因为在很多代码里总是看到这个关键字,但又没有具体的了解下,所以在这里梳理一下。

首先看一个经典例子

可见性例子:
package com.study.juc.testvolatile;

/**
 * Created by yangqj on 2017/8/26.
 */
public class TestVolatile {
    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        new Thread(threadTest).start();

        while (true){
            if(threadTest.isFlag()){
                System.out.println("flag的值被修改了");
                break;
            }
        }
    }
}

class ThreadTest implements Runnable{
    private boolean flag = false;

    @Override
    public void run() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag="+isFlag());
    }

    public boolean isFlag() {
        return flag;
    }

}

代码很简单,就是主线程和子线程共同访问同一个变量flag,子线程修改了flag的值,然后主线程循环判断flag的值修改了,就break 结束了。

正常逻辑下应该是期望输出:

flag的值被修改了
flag=true

可是运行后发现结果为:

flag=true

可以看到子线程明明修改了flag的值,但主线程还是并没有输出“flag的值被修改了”主线程也一直未结束,进入了死循环。

此时我们在给flag再上volatile关键字:

private volatile boolean flag = false;

然后再运行后发现程序输出正常结果。为什么呢?

说明

计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。

也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。(以上文字拷贝网上)

Java内存模型并没有限制执行引擎使用处理器的寄存器或者高速缓存来提升指令执行速度,也没有限制编译器对指令进行重排序。也就是说,在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。

总结以上,得出有两块内存,一个是主存,一个是CPU自己的告诉缓存,也就是工作内存。我们画个图
image

我们上面做的异常的例子的主要原因就是这个工作内存和主内存的值不一致导致的。
整个流程为:

1.主线程读取主内存的flag为false,主线程的工作内存的flag为false。逻辑一直执行while循环
2.子线程修改子线程的工作内存中的flag为true,并且将主内存的flag设置为true。(特地将子线程睡眠300就是为了让子线程在主线程读取了falg为false后再执行,为了放大问题)
3.主线程因为while循环的高效,并没有时间去读取主内存的flag,一直读取工作内存中的flag,所以一直读取的值为false。所以一直循环下去,导致此现象。

而volatile关键字有一个特性就是保证了实例变量在多个线程之间的可见性。通过volatile关键字,强制的从主内存中读取变量的值。执行逻辑如图:
image

如图所示,线程主题读取的直接是主内存,所以主线程读取到的值肯定是最新的,所以加了volatile之后,正常正常运行。但也因为放弃了工作内存,执行效率上会有一定的下降。
这个现象,主线程没有第一时间读取到最新的flag值的问题也被称为内存的可见性。 当多个线程操作共享数据时,彼此不可见。

原子性例子

volatile关键字有一个致命的缺点就是不支持原子性
请看例子:

package com.study.juc.testvolatile;

import com.thread.PCOne2Many.Run;

/**
 * Created by yangqj on 2017/8/26.
 */
public class TestAtomic {
    public static void main(String[] args) {
        AtomicDemo atimoc = new AtomicDemo();
        for (int i = 0; i < 10; i++) {
            new Thread(atimoc).start();
        }
    }


}

class AtomicDemo implements Runnable{
    private int count =0;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getCount());
    }

    public int getCount(){
        return count++;
    }
}

程序逻辑也是很简单:
主线程开启了10个子线程,10个子线程都去读取一遍AtimocDemo的count值,然后+1.
正常情况下,应该是输出0-9
然而运行结果如下:

0
2
4
0
5
1
0
3
6
7

因为count++的操作并不是原子的
1.需要从内存中读取count值
2.把count加1
3.再更新内存中的count值

从结果中看到有3个0输出,也就是说
1.当1线程读取到count值为0 ,打印为0,并+1后更新主存
2.在1线程未更新主存之前,有另外两个线程2和3线程也读取了原先主存中count的值为0,并且打印了。所以此时3个线程都将主内存的count值设置为1。

这就是原子性,而volatile 关键字不具有互斥性,多个线程可以同时访问,所以也就不能保证变量的原子性

如果要保证其原子性,可以使用相较于synchronized,保证了互斥性,也就保证了原子性。也可以使用java.util.concurrent.atomic包下的常用原子变量。

最后将关键字volatile和synchronized比较一下:
1.关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且vilatile只能修饰于变量,而synchronized可以修饰方法,已经代码块。
2.多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
3.volatile能保证数据的可见性,但不能保证数据的原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将工作内存和主内存的数据做同步。
4.volatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性。

【验证技能树】UVM 源码解读01 -- UVM本质上就是软件工程方法论在芯片验证领域的应用 文章摘要: UVM(通用验证方法学)是基于SystemVerilog的芯片验证框架,采用组件化、面向对象和事务级抽象设计。其核心在于职责分离与数据流,通过Driver、Monitor、Sequencer等模块协作实现高效验证。UVM强调复用性、可扩展性和可维护性,类似软件工程的模块化与低耦合原则。理解其体系结构比记忆API更重要,有助于灵活组合组件并提升验证效率。UVM不仅是工具,更融合了软件工程思维,推动验证工作向自动化、系统化发展。掌握UVM底层逻辑能优化验证环境设计,适用于从IP到SoC的多层级验证场 阅读详情

相关推荐

从零开始安装 Windows 11 和 Ubuntu 22.04 双系统

本文详解Windows与Ubuntu 22.04双系统安装流程:需提前确认BIOS模式(UEFI/GPT或Legacy/MBR),关闭BitLocker加密、设备加密及快速启动;推荐Rufus/Ventoy制作启动盘。安装时需规划分区方案(建议单独划分EFI系统分区),注意引导器安装位置选择。双系统启动通过BIOS引导顺序管理,安装后需同步Windows与Linux系统时间。教程适配主流UEFI+GPT设备,强调操作前数据备份及Windows更新策略调整,避免兼容性问题。

萑澈的博客 3777

Java Volatile关键字

Java的volatile关键字用于标记一个变量“应当存储在主存”。更确切地说,每次读取volatile变量,都应该从主存读取,而不是从CPU缓存读取。每次写入一个volatile变量,应该写到主存中,而不是仅仅写到CPU缓存。实际上,从Java 5开始,volatile关键字除了保证volatile变量从主存读写外,还提供了更多的保障。我将在后面的章节中进行说明。

m0_72885838的博客 3403

ESP-01S获取网络时间踩坑全记录:AT指令调试、JSON解析乱码、时区处理,一个都不能少

本文详细记录了使用ESP-01S WIFI模块获取网络时间的完整过程,包括AT指令调试、JSON解析乱码处理以及时区转换等关键步骤。通过实战案例和代码示例,帮助开发者避开常见陷阱,实现稳定的网络时间同步功能。

weixin_30460489的博客 295

【Java并发编程】关键字volatile详解

详细解析了 Java 中 volatile 关键字的原理和应用,探讨了其在多线程环境下如何保证变量可见性和有序性,以及无法保证原子性的原因。

叉烧钵钵鸡的博客 2640

钙钛矿太阳能电池稳定性测试:ISOS协议详解与标准化实践指南

在光伏器件研发中,稳定性测试是评估材料与器件长期可靠性的核心环节,其关键在于实现测试条件的标准化与数据的可比性。通过控制变量和模拟真实环境应力,标准化测试协议能够系统揭示材料衰减机理与器件失效模式,为技术迭代提供可靠依据。在钙钛矿太阳能电池领域,ISOS协议作为国际公认的测试框架,通过分级体系(如ISOS-L系列光照测试与ISOS-D系列暗态测试)和关键参数定义(如光源光谱匹配、MPP跟踪、结温控制),解决了不同实验室数据无法横向对比的痛点。该协议不仅规范了测试流程,还通过硬件系统搭建、自动化数据采集与归一

weixin_33600816的博客 324

永不消逝的电波 -GSM空口信号的接收与解码

一开始接触软件无线电应该是从很早之前对RFID洗衣卡的解密开始的,后来接触人员定位,慢慢的打开了这样一扇大门。。。。 翻到之前做的GSM信号的接收解码,今天就对其过程做一个记录,也不是什么新颖的内容,权当是记录和回忆了。 对于GSM网络的相关内容,不是非常的熟悉,如有不正确的,欢迎批评指正。

Jack.Xu 专栏 1万+

java基础—Volatile关键字详解

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

江哈哈的博客 4715

K8s入门教程(一)

本教程提供了Kubernetes(K8s)的入门指南,涵盖了从基础概念到实战操作的全面内容。首先介绍了Kubernetes的定义和核心功能,如自动化部署、弹性伸缩、自我修复等。接着,详细讲解了如何在macOS上安装Minikube和kubectl工具,以及如何使用这些工具创建和管理Pod、Deployment和Service等核心资源。教程还包括了实战案例,如部署Nginx应用,并介绍了高级特性如滚动更新和自动伸缩。最后,提供了常见问题的解决方案,帮助用户快速上手和解决实际使用中的问题。

2401_82505179的博客 1691

Java关键字——volatile

1、JMM(java内存模型) 要了解volatile是什么,首先需要知道JMM,JMM全称是java内存模型(Java Memory Mode)。 为什么需要java内存模型,是因为在不同的硬件生产商和不同的操作系统下,内存的访问逻辑有一定的差异,结果就是当你的代码在某个系统环境下运行良好,并且线程安全,但是换了个系统就出现各种问题。Java内存模型,就是为了屏蔽系统和硬件的差异,让一套代码在不同平台下能到达相同的访问结果。 JVM中将内存划分为主内存和工作内存。 主内存对应java堆中的对象实例,

weixin_43962358的博客 924

基于YOLOv12的牙齿识别与计数系统:从理论到完整实现

本文详细介绍了一种基于YOLOv12深度学习模型的牙齿识别与计数系统。该系统能够在口腔X光片中准确识别和计数牙齿,并提供了用户友好的UI界面。文章涵盖了数据预处理、模型训练、性能优化以及完整系统实现的各个环节,并提供了可复现的代码和数据集参考。整个系统包含三个主要模块:数据预处理模块YOLOv12模型训练模块UI界面展示模块。

YOLO 180

人工智能算法面试大总结-总目录

涉及秋招、春招、社招

啥都生的博客 2万+

java volatile 关键字详解

java volatile 关键字详解 一,什么是volatile关键字,作用是什么 ​ volatile是java虚拟机提供的轻量级同步机制 ​ 作用是: 1.保证可见性 2.禁止指令重排 3.不保证原子性 本篇具体就讲解 什么叫保证了可见性, 什么叫禁止指令重排,什么是原子性 而在这之前需要对JMM 有所了解 二,什么是JMM ​ JMM(java 内存模型 Java Memory Model 简称JMM) 本身是一个抽象的概念,并不在内存中真实存在的,它描述的

clinginh的博客 9725

英飞凌Tricore问题排查01_Det/Reset/Trap排查宝典

软件跑飞进Det或发生Reset或进Trap了,怎么查?有固定套路可以follow吗?英飞凌Tricore又有哪些专门的定位办法?本篇为您娓娓道来。

initiallizer的博客 4417

java中的Volatile关键字使用

文章目录什么时候使用volatileHappens-Before java中的Volatile关键字使用 在本文中,我们会介绍java中的一个关键字volatile。 volatile的中文意思是易挥发的,不稳定的。那么在java中使用是什么意思呢? 我们知道,在java中,每个线程都会有个自己的内存空间,我们称之为working memory。这个空间会缓存一些变量的信息,从而提升程序的性能。当...

程序那些事 1万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java volatile 关键字

同一个成员变量在不同线程中处于各自独立的虚拟机栈帧中,A 线程修改了该变量,B 线程中读的值可能仍然是其内存缓存的值。不管是 cpu 还是虚拟机,都会对操作指令进行重新排序以到达最佳的执行效果,volatile 针对 JVM 指令禁止重排序,达到避免因指令重排在某些边界条件下出现的不符合预期的问题。这是一个典型的双重校验的懒汉式模的单例模式,但这里隐藏着一个重大风险,就是在多线程访问下,在创建之初,某个线程会获取到一个没有执行。在禁止指令重排上的使用场景,接下来再看看确保变量的内存可见性的场景,在。

HJXASLZYY的专栏 773

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Java 关键字 volatile

是 Java 中的一个关键字,用于修饰变量,确保多线程环境下的可见性和有序性。它主要用于解决以下两个问题:在多线程环境中,每个线程都有自己的工作内存(缓存),线程对变量的操作通常是在工作内存中进行的。如果没有同步机制,一个线程对变量的修改可能不会立即反映到主内存中,其他线程也就无法看到最新的值。 问题: 效果:Java 编译器和处理器可能会对指令进行重排序以优化性能,但这可能导致多线程环境下的行为不符合预期。 可以禁止指令重排序,确保代码的执行顺序符合程序员的意图。 问题: 效果: 解决方案:状态标志:

体系化很难,碎片归纳很简单。 1248

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

java中的volatile 关键字

在 Java 中,volatile是一个类型修饰符(keyword),用于修饰变量。它的主要作用是确保变量的​​和​​,从而在多线程环境下提供一定程度的线程安全保障。volatile关键字提供了一种轻量级的同步机制,适用于某些特定的并发场景,但其功能有限,不能替代诸如等更强大的同步工具。

qq_57019146的博客 1264

Java中volatile关键字详解

本文深入解析了Java中volatile关键字的核心机制和实际应用。文章首先介绍了volatile的两大主要特性——保证线程间共享变量的可见性和禁止指令重排序,从而确保多线程环境下数据操作的顺序性。随后,通过具体代码案例展示了volatile在控制线程停止、单例模式中防止指令重排序以及其在复合操作中可能出现的问题

欢迎来到我的技术博客 1620

java中volatile关键字的含义_Java里volatile关键字是什么意思

在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉。Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。synchronized同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程...

weixin_42350514的博客 347

Java 中 volatile 关键字的作用是什么?

关键字的理解,包括它的作用、特性(可见性、禁止指令重排)以及局限性(不保证原子性)。回答时需要清晰地解释这些概念,并提供一些代码示例来说明问题。理解这些联系可以帮助你更全面地掌握 Java 并发编程的知识,并了解如何在实际应用中选择合适的同步机制。是 Java 中的一个关键字,用于修饰变量。内存屏障 (Memory Barrier)

qq_63553317的博客 2014

Java volatile关键字

作用及原理 注意事项

黑曼巴 7724

Java 中的volatile关键字

Java的volatile关键字是一种轻量级的线程同步机制,主要保证变量的可见性和有序性。它确保变量修改后立即刷新到主内存,禁止指令重排序,但不保证原子性。典型应用场景包括状态标志、双重检查锁定单例模式等。与synchronized相比,volatile性能开销更小但功能有限,适合单一变量、一写多读的场景。使用时需注意其无法保证复合操作的原子性,建议优先考虑不可变对象或原子类。正确使用volatile能有效解决多线程环境下的内存可见性问题。

无论云泥意贯一 1322

浅析Java中volatile关键字

volatile是一种轻量级锁的实现,它针对的仅仅是共享变量,不会对线程加锁,更不会造成线程的阻塞。volatile保证内存可见性,当变量值更新时,其他共享该变量的线程从主内存获取最新的值。volatile通过内存屏障禁止指令重排序。volatile对于复合赋值操作不保证原子性,如果需要保证原子性,还是需要使用synchronized、java.util.concurrent中的锁或原子类。

进步新青年的博客 1299

Java中的volatile关键字详解

volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。 Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此...

刘成 493

Java并发:volatile关键字详解

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

2401_84414763的博客 992

java 关键字 volatile,Java Volatile关键字

Java的volatile关键字用于标记一个变量“应当存储在主存”。更确切地说,每次读取volatile变量,都应该从主存读取,而不是从CPU缓存读取。每次写入一个volatile变量,应该写到主存中,而不是仅仅写到CPU缓存。实际上,从Java 5开始,volatile关键字除了保证volatile变量从主存读写外,还提供了更多的保障。我将在后面的章节中进行说明。变量可见性问题Java的vola...

weixin_40002238的博客 161
上一篇: flume入门 log4j 输出日志到flume
下一篇: nginx 安装 以及添加ldap模块
poorCoder_
博客等级 码龄10年 129粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值