java多线程(六)关键字Volatile可见性、有序性以及单个变量的原子性

news2025/1/11 2:50:54

volatile关键字

作用

volatile 是 Java 虚拟机提供的轻量级的同步机制,主要用来确保变量被线程安全地读取和写入。

当一个变量定义为 volatile 后,它具备以下特性:

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

  2. 有序性:禁止进行指令重排序优化。

实现原理

在 Java 内存模型中,每个线程在运行时都有自己的工作内存,其中存放着主内存中变量副本的拷贝。

当线程对变量进行修改时,首先修改的是自己工作内存中的副本,然后线程再将修改后的副本值刷新到主内存中。

当其他线程需要读取该变量时,会从主内存中重新读取最新的副本到自己的工作内存中。

volatile 变量的特殊规则如下:

  • 每次使用前都需要从主内存中刷新最新的值,保证读取的总是最新的数据。

  • 每次修改后都需要立即同步回主内存中,保证其他线程可以看到最新的值。

使用场景
  1. 状态标记量:如中断标记、状态标记等。

  2. 单例模式的双重检查锁定:防止指令重排序。

注意事项
  1. volatile 变量不适合作为状态的复合操作,因为 volatile 不能保证复合操作的原子性。

  2. volatile 只能保证变量的可见性和有序性,不能保证原子性。

volatile示例讲解

1. 可见性

Volatile保证了变量的可见性,即当一个线程修改了某个变量的值,这个新值对其他线程来说是立即可见的。

反面代码示例:不使用volatile时,可能导致线程读取到过时数据。

package com.hmblogs.backend.study.thread;

public class VolatileExample {
    // 假设threadA和threadB同时访问isFlag变量,且没有使用volatile关键字
    static boolean isFlag = false;
    public static void main(String[] args) throws InterruptedException {


        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            isFlag = true;
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            if (!isFlag) {
                // 这里的代码可能会执行,即使isFlag已经被thread1设置为true
                System.out.println("isFlag 是 false.");
            }
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

正面代码示例:使用volatile确保变量值的实时更新。

package com.hmblogs.backend.study.thread;

public class VolatileExample {
    // 假设threadA和threadB同时访问isFlag变量,且没有使用volatile关键字
    volatile static boolean isFlag = false;
    public static void main(String[] args) throws InterruptedException {


        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            isFlag = true;
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            if (!isFlag) {
                // 这里的代码可能会执行,即使isFlag已经被thread1设置为true
                System.out.println("isFlag 是 false.");
            }
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

验证结果:volatile没有对应的特性。这是什么原因呢?

2. 原子性

Volatile对单个变量的读写具有原子性,但复合操作(如i++)不是原子的。

反面代码示例:volatile变量在复合操作中可能出现问题。

package com.hmblogs.backend.study.thread;

public class VolatileExample {
    static volatile int counter = 0;
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            counter++;
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            System.out.println("counter 是 "+counter+".");
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

有时候打印的是1,有时候是0

正面代码示例:使用synchronized或原子类来保证复合操作的原子性。

package com.hmblogs.backend.study.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileExample {
    static volatile AtomicInteger counter = new AtomicInteger();
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            // thread1中执行
            int temp = counter.getAndAdd(1);
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            // thread2中执行
            System.out.println("counter 是 "+counter.get()+".");
        });

        thread1.start();
        thread2.start();

        System.out.println("Both threads have finished their execution");
    }
}

一直都是0.

验证结果:volatile没有对应的特性。这是什么原因呢?

3. 有序性

Volatile变量还可以确保指令的有序性,防止JVM进行指令重排序。

反面代码示例:不使用volatile时,JVM可能进行指令重排序。

int a = 0;
boolean flag = false; // 假设这里不使用volatile
int b = 0;

// 线程A执行
a = 1; // Step 1
flag = true; // Step 2
b = 2; // Step 3

// 线程B执行
if (flag) {
    // JVM可能重排序,导致线程B先看到b=2,再看到a=1
}

 正面代码示例:使用volatile防止指令重排序。

int a = 0;
volatile boolean flag = false;
int b = 0;

// 线程A执行
a = 1; // Step 1
flag = true; // Step 2,volatile变量,确保前面操作不会重排序到后面
b = 2; // Step 3

// 线程B执行
if (flag) {
    // 确保先看到a=1,再看到b=2
}

最后

Volatile是Java多线程编程中的一个重要关键字,它确保了变量的可见性、有序性和部分原子性。

正确使用volatile关键字可以避免多线程中的一些问题,如数据不一致和指令重排序等。

然而,需要注意的是,volatile并不能保证复合操作的原子性,对于这类操作,需要使用synchronized或原子类来保证线程安全。

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

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

相关文章

自存实践本地访问 nginx放前端打包好的项目

nginx 部署前端项目_哔哩哔哩_bilibili 将打包好的dits文件放到 配置nginx.conf文件的location 启动命令 start nginx.exe 输入localhost即可访问打包好的项目 nginx的特点 1.静态资源 2.转发 设置代理转发请求 关闭nginx .\nginx.exe -s quit

Kubernetes-Pod调度基础

一.复制控制器(ReplicationController,RC) RC用来确保Pod副本数达到预期值,这样可以确保一个或多个同类Pod总是可用的。可以通过扩缩来增加或减少pod。 (1)示例: vim replicationcontroller-ng…

Codeforces Round 967 (Div. 2)

文章目录 A. Make All Equal题目描述思路代码 B. Generate Permutation题目描述思路代码 C. Guess The Tree题目描述思路代码 A. Make All Equal 题目描述 一个数组,最多实行n-1次,计算最少多少次可以变为同一个数 思路 计算重复次数最多的数&#x…

产业园区数字化转型:面对挑战,我们如何把握机遇加速前行?

在当今数字化的时代浪潮中,产业园区数字化转型已成为推动经济发展和提升竞争力的关键举措。然而,这一进程并非坦途,充满了各种挑战。 产业园区数字化转型面临着技术更新换代快的压力。新技术不断涌现,如物联网、人工智能、大数据…

Mybatis 速通秘籍 节省回顾知识点和学习成本

目录 一、MyBatis简介 1、MyBatis历史 2、MyBatis特性 3、MyBatis下载 4、和其它持久层技术对比 二、搭建MyBatis 1、开发环境 2、创建maven工程 a>打包方式 b>引入依赖 3、创建MyBatis的核心配置文件 4、创建mapper接口 5、创建MyBatis的映射文件 6、通过j…

python学习之路 - python的异常、模块与包

目录 一、python的异常、模块与包1、了解异常2、异常的捕获方法a、捕获基本异常b、捕获指定异常c、捕获多个异常d、捕获异常后的finally 3、异常的传递4、python模块a、定义b、基础语法c、使用方法d、补充 5、python包a、定义b、操作方法c、使用方法 6、安装第三方python包a、命…

MVC和三层架构

👉参考文章:mvc简介,mvc与三层架构的区别 一.MVC是什么? Model-View-Controller(MVC)是一种软件架构模式,是软件设计模式的体现 ,用于组织代码并分离关注点,广泛应用于…

GPS和桩号互转

文章目录 前言一、通过bigmap软件生成坐标信息csv二、Java实现1.CSV分隔2.计算2.1 读取gps_data.csv2.2 读取piles.csv2.3 进行线性插值2.4 返回值实体2.5 根据GPS坐标计算距离工具2.6 根据GPS坐标读取桩号2.7 根据桩号读取GPS坐标(根据距离计算,找到最近的桩号) 前…

短视频SDK解决方案,智能技术加持,提升创作效率

随着社交媒体、直播电商、在线教育等领域的蓬勃发展,短视频以其独特的魅力迅速崛起,成为内容创作与传播的新风口。为了助力企业和个人轻松拥抱视频化趋势,美摄科技匠心打造了一套高效、易用的短视频SDK解决方案,以“轻编辑&#x…

【高级IO-2】IO多路转接之Select(概念及代码实例)

文章目录 I/O 多路转接 之 Select1. 了解select2. select 函数原型① fd_set 结构② 详细理解参数(readfds为例) 3. 理解select的执行过程4. select代码实例:监视多个文件描述符5. Socket就绪条件6. select代码实例:多路复用服务器…

每日掌握一个科研插图·2D密度图|24-08-21

小罗碎碎念 在统计学和数据可视化领域,探索两个定量变量之间的关系是一种常见的需求。为了更深入地理解这种关系,我们可以使用多种图形表示方法,这些方法在本质上是对传统图形的扩展和变体。 散点图:这是最基本的图形&#xff0c…

什么是 JavaConfig?

什么是 JavaConfig? 💖The Begin💖点点关注,收藏不迷路💖 JavaConfig是Spring框架的一项创新,它允许开发者使用纯Java代码来配置Spring IoC容器,从而避免了繁琐的XML配置。这一特性带来了诸多优…

【微信小程序】导入项目

1.在微信开发工具中,点击【导入项目】 2.在打开的界面中执行2个步骤 1.找到要导入项目的路径2.AppID要改成自己的AppID 3.package.json包初始化【装包之前要确保有package.json文件】 1.在【资源管理器】空白处,点击鼠标右键,选择【】&am…

免费的真是太香了!Chainlit接入抖音 Coze AI知识库接口快速实现自定义用户聊天界面

前言 由于Coze 只提供了一个分享用的网页应用,网页访问地址没法自定义,虽然可以接入NextWeb/ChatGPT web/open webui等开源应用。但是如果我们想直接给客户应用,还需要客户去设置配置,里面还有很多我们不想展示给客户的东西怎么办…

[Python可视化]空气污染物浓度地图可视化

[Python可视化]空气污染物浓度地图可视化,果然是路边浓度最大 在本篇文章中,我将展示如何使用 Python 结合 OSMnx、NetworkX 和 GeoPandas 等库,计算给定路径的最短路线,并基于该路径穿过的网格单元计算总污染量。最终&#xff0c…

uniapp 修复使用 uni.saveImageToPhotosAlbum 方法在部分安卓手机上保存失败

场景:使用 uni.saveImageToPhotosAlbum 保存图片,其他手机都是可以的,但在鸿蒙系统的手机上出现了bug,报错Object {errMsg:"savelmageToPhotosAlbum:fai..errMsg:savelmageToPhotosAlbum:fail invalid filetype"} 原因&…

数学建模学习(116):全面解析梯度下降算法及其在机器学习中的应用与优化

文章目录 1.梯度下降简介1.1 梯度下降的数学原理1.2 学习率的选择2 梯度下降变体3.梯度下降优化器3.1 动量法(Momentum)3.2 AdaGrad3.3 RMSprop3.4 Adam3.5 Python 使用不同优化器训练线性回归模型4.案例:使用梯度下降优化加利福尼亚房价预测模型4.1. 数据准备4.2. 模型训练…

【dotnet】Ubuntu 24.04安装dotnet 8.0报错

我的环境是Ubuntu 24.04,64位,使用azure的虚拟机。 报错文字如下: kidfuazurefu:~$ sudo apt install dotnet-sdk-8.0 Reading package lists... Done Building dependency tree... Done Reading state information... Done Some packages c…

数学生物学-3-固定点、稳定性和蛛网图(Fixed Points, Stability, and Cobwebbing)

在前一篇博客中,我们研究了一些离散时间模型的例子。特别是,我们推导出了离散逻辑方程的重要例子。 数学生物学-2-离散时间模型(Discrete Time Models)-CSDN博客 在本篇文章中,我们将考虑离散时间模型的一般形式(在数…

超声波水表是什么?量程比又是什么?

一、超声波水表概述 1.定义: 超声波水表是一种利用超声波技术来测量水流速度,进而计算出流经管道的水体积流量的计量设备。它通过发送和接收超声波信号的时间差来确定水流的速度,从而精确地计量水的流量。 2.工作原理: 超声波…