一文学会Volatile关键字

引言

在 Java 多线程实战中,volatile 是一个重要的关键字,用于修饰变量,经常在JUC源码中出现,本文详细解析一下这个关键字的奥秘

1. 基本概念

volatile 关键字的主要作用是保证变量的可见性以及在一定程度上禁止指令重排序。在多线程环境下,它可以确保一个线程对被 volatile 修饰的变量所做的修改能立即被其他线程看到。

2. 内存可见性

2.1 Java 内存模型(JMM)基础

在 Java 内存模型中,每个线程都有自己的工作内存,线程对变量的操作(读取、赋值等)都是在工作内存中进行的,而变量的实际存储位置是主内存。当一个线程修改了某个变量的值,它首先会将修改后的值存储在自己的工作内存中,之后才会在某个时刻将这个值刷新到主内存。其他线程读取该变量时,也是先从自己的工作内存中读取,如果工作内存中的值不是最新的,就会导致数据不一致的问题。

2.2 volatile 保证可见性的原理

当一个变量被声明为 volatile 时,对该变量的写操作会强制将修改后的值立即刷新到主内存中,而读操作会强制从主内存中读取最新的值。这样,当一个线程修改了 volatile 变量的值,其他线程能够立即看到这个修改。

public class VolatileVisibilityExample {// 使用 volatile 修饰变量private static volatile boolean flag = false;public static void main(String[] args) {// 启动一个线程修改 flag 的值new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}flag = true;System.out.println("Flag is set to true");}).start();// 主线程不断检查 flag 的值while (!flag) {// 循环等待}System.out.println("Flag is now true, exiting loop");}
}

在上述代码中,如果 flag 变量没有被声明为 volatile ,主线程可能会一直处于循环中,因为它看不到另一个线程对 flag 的修改。而使用 volatile 修饰后,主线程能够及时看到 flag 的变化,从而退出循环。

3. 禁止指令重排序

3.1 指令重排序概念

在 Java 中,为了提高程序的执行效率,编译器和处理器会对指令进行重排序。指令重排序可以分为编译器重排序和处理器重排序。在单线程环境下,指令重排序不会影响程序的最终执行结果,但在多线程环境下,指令重排序可能会导致数据不一致的问题。

3.2 volatile 禁止指令重排序的原理

volatile 关键字可以禁止指令重排序,它通过内存屏障来实现。内存屏障是一种特殊的指令,它可以确保在屏障之前的指令不会被重排序到屏障之后,反之亦然。

public class VolatileReorderingExample {private static int a = 0;private static volatile boolean flag = false;public static void writer() {a = 1;          // 1flag = true;    // 2}public static void reader() {if (flag) {     // 3int i = a;  // 4System.out.println(i);}}
}

在上述代码中,由于 flag 被声明为 volatile,编译器和处理器不会将 flag = true 重排序到 a = 1 之前,从而保证了 reader 方法在 flag 为 true 时能够读取到 a 的最新值。

4. volatile存在的局限性

虽然 volatile 关键字可以保证变量的可见性和一定程度上禁止指令重排序,但它并不能保证原子性。例如,对于 i++ 这样的操作,它实际上包含了读取、加 1 和写入三个操作,这三个操作不是原子的。即使 i 被声明为 volatile,在多线程环境下,仍然可能会出现数据不一致的问题。如果需要保证原子性,需要使用 synchronized 关键字或 Atomic 类。

总结

volatile 是 Java 里用于修饰变量的关键字,其核心作用体现在保证变量的内存可见性以及一定程度上禁止指令重排序,在多线程编程中扮演着重要角色。

在 Java 内存模型的框架下,volatile 确保对变量的写操作会立即将修改后的值刷新到主内存,读操作则强制从主内存获取最新值,有效避免了因线程工作内存与主内存数据不一致而引发的问题。 通过内存屏障机制,volatile 能够禁止编译器和处理器对指令进行重排序,保障了多线程环境下代码按照预期顺序执行,避免因重排序导致的数据不一致问题。

volatile 存在局限性,它无法保证操作的原子性。像 i++ 这类复合操作,在多线程环境中即便使用 volatile 修饰,依然可能出现数据不一致的情况。

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

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

相关文章

Java测试框架Mockito快速入门

Mockito结合TestNG快速入门 什么是Mockito Mockito 是一个专门用于 Java 的强大测试框架,主要用来创建和管理模拟对象,辅助开发者进行单元测试,具有以下特点和功能: 创建模拟对象:能通过简洁的语法创建类或接口的模…

week 3 - More on Collections - Lecture 3

一、Motivation 1. Java支持哪种类型的一维数据结构? Java中用于在单一维度中存储数据的数据结构,如arrays or ArrayLists. 2. 如何在Java下创建一维数据结构?(1-dimensional data structure) 定义和初始化这些一…

Ubuntu 防火墙iptables和 ufw

文章目录 iptables 和 ufw 的区别Ubuntu 上使用 ufw 配置 iptables 和 ufw 的区别 iptables 和 ufw 是 Linux 系统中用于管理防火墙的工具,但它们的设计目标和使用方式有所不同。 iptables:功能强大,适合高级用户和复杂场景,但配…

(动态规划 最长连续递增子序列)leetcode 674

我上个文章提到了最长递增子序列这个题可以去看看 这个题目翻译人话就是找出最长的递增子串,用一层for循环就行,时间复杂度是O(n) 比起上个题,一个范围多条子序列(路径)这里一个范围只有一条递增路径,所以…

STM32CubeMx DRV8833驱动

一、DRV8833驱动原理 ​ STBY口接单片机的IO口,STBY置0电机全部停止,置1才能工作。STBY置1后通过AIN1、AIN2、BIN1、BIN2 来控制正反转。 AIN1AIN2电机状态00停止1speed反转speed1正转11停止 其中A端(AIN1与AIN2)只能控制AO1与…

JSON Schema 入门指南:如何定义和验证 JSON 数据结构

文章目录 一、引言二、什么是 JSON Schema?三、JSON Schema 的基本结构3.1 基本关键字3.2 对象属性3.3 数组元素3.4 字符串约束3.5 数值约束 四、示例:定义一个简单的 JSON Schema五、使用 JSON Schema 进行验证六、实战效果6.1 如何使用 七、总结 一、引…

前端Npm面试题及参考答案

目录 npm 是什么?它的主要作用是什么? npm 包管理工具与 Yarn 有何不同? npm 的 package.json 文件有哪些重要字段? 什么是 npm 依赖?如何在项目中安装、更新和移除依赖? npm 的 node_modules 目录是什么?它的作用是什么? 什么是 npm 脚本?如何在 package.json 中…

零样本思维链(Zero-shot CoT)

Large Language Models are Zero-Shot Reasoners (Kojima et al., 2022) 这篇文章研究了大型语言模型 (LLMs) 在推理任务上的能力,并提出了一种名为 Zero-shot-CoT 的新方法,该方法能够有效地引导 LLM 进行多步骤推理,并在各种推理任务上取得…

day01_Java基础

文章目录 day01_Java基础一、今日课程内容二、Java语言概述(了解)1、Java语言概述2、为什么要学习Java语言3、Java平台版本说明4、Java特点 三、Java环境搭建(操作)1、JDK和JRE的概述2、JDK的下载和安装3、IDEA的安装4、IDEA的启动…

设计模式 之 生产消费者模型 (C++)

文章目录 设计模式 之 生产消费者模型 (C)引言生产消费者模型的基本概念为什么需要生产消费者模型应用场景:C 实现生产消费者模型代码示例代码详细解释共享资源和同步机制生产者函数 producer()消费者函数 consumer()主函数 main() 注意事项总…

Spring Boot 项目开发流程全解析

目录 引言 一、开发环境准备 二、创建项目 三、项目结构 四、开发业务逻辑 1.创建实体类: 2.创建数据访问层(DAO): 3.创建服务层(Service): 4.创建控制器层(Controller&…

数据结构课程设计(java实现)---九宫格游戏,也称幻方

【问题描述】 九宫格,一款数字游戏,起源于河图洛书,与洛书是中国古代流传下来的两幅神秘图案,历来被认为是河洛文化的滥觞,中华文明的源头,被誉为"宇宙魔方"。九宫格游戏对人们的思维锻炼有着极大…

GPT-4.5 怎么样?如何升级使用ChatGPTPlus/Pro? GPT-4.5设计目标是成为一款非推理型模型的巅峰之作

GPT-4.5 怎么样?如何升级使用ChatGPTPlus/Pro? GPT-4.5设计目标是成为一款非推理型模型的巅峰之作 今天我们来说说上午发布的GPT-4.5,接下来我们说说GPT4.5到底如何,有哪些功能?有哪些性能提升?怎么快速使用到GPT-4.…

【vscode-解决方案】vscode 无法登录远程服务器的两种解决办法

解决方案一: 查找原因 命令 ps ajx | grep vscode 可能会看到一下这堆信息(如果没有大概率不是这个原因导致) 这堆信息的含义:当你使用 vscode 远程登录服务器时,我们远程机器服务端要给你启动一个叫做 vscode serv…

一、对4*3按键模块编程分析

一、4*3键盘模块实物分析 说明: 1、横着4排,竖着3列,加起来共7组,所以对外引出7根线。 2、根据排针终端引脚又可分两类。即横排和竖列对应的引脚。 二、代码编写构想: 1、使用7个gpio输入中断,检测7个…

自然语言处理NLP入门 -- 第十节NLP 实战项目 2: 简单的聊天机器人

一、为什么要做聊天机器人? 在互联网时代,我们日常接触到的“在线客服”“自动问答”等,大多是以聊天机器人的形式出现。它能帮我们快速回复常见问题,让用户获得及时的帮助,并在一定程度上减少人工客服的压力。 同时&…

linux(1)文件管理

文章目录 文件目录系统相对路径绝对路径命令解析器文件管理 文件目录系统 bin: 二进制文件目录,存储可执行文件 dev:设备目录,所有的硬件都会抽象成文件存储,比如鼠标键盘 home:存储普通用户的家目录 li…

CSS—选择器详解:5分钟动手掌握选择器

个人博客:haichenyi.com。感谢关注 1. 目录 1–目录2–引言3–种类4–优先级 引言 什么是选择器? CSS选择器是CSS(层叠样式表)中的一种规则,用于指定要应用样式的HTML元素。它们就像是指向网页中特定元素的指针&#…

大模型微调入门(Transformers + Pytorch)

目标 输入:你是谁? 输出:我们预训练的名字。 训练 为了性能好下载小参数模型,普通机器都能运行。 下载模型 # 方式1:使用魔搭社区SDK 下载 # down_deepseek.py from modelscope import snapshot_download model_…

DeepSeek实战

DeepSeek 接入实战:从零开始快速上手 引言 在当今的 AI 领域,DeepSeek 作为一个强大的自然语言处理(NLP)平台,提供了丰富的 API 接口,帮助开发者快速实现智能对话、文本生成、语义分析等功能。本文将带你…