Volatile关键字的作用探究

news2024/10/6 4:05:45

前言

今天下午BOSS上投了个简历小试了一波水,结果被问到一个知识点volatile关键字的作用,我回答了线程的可见性,另一个死活想不起来是什么,当回到工位上看了眼笔记,才想起来。这种知识点其实平时使用的频率还是挺高的,但是一般很少去探究它的原理,导致今天吃了亏。

Volatile关键字是什么?

Volatile是java提供对内存模型访问的一些特殊的访问规则。

当一个变量被定义为volatile时,该变量将有两成含义
1、线程间的可见性。
当一个线程对一个变量做出改变时,另一个线程可以使用到被改变后的值。但是呢,这里有个问题,先写一段测试代码看下:

public class ThreadTest {

    private static int j = 10;
    public static void main(String[] args) {

        testNoVolatile();
    }

    private static void testNoVolatile() {

        for (int i=0; i<10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    ThreadUtils.doSleep(10000L);
                    System.out.println(j--);
                }
            });
            thread.start();
        }
    }
}

运行结果:

image.png
当添加了volatile关键字之后:

public class ThreadTest {

    private static volatile int j = 10;
    public static void main(String[] args) {

        testVolatile();
    }

    private static void testVolatile() {

        for (int i=0; i<10; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    ThreadUtils.doSleep(10000L);
                    System.out.println(j--);
                }
            });
            thread.start();
        }
    }
}

image.png

发现了什么问题,加了volatile和没加一样,跑出来的都不是我想要的,所以这里一直存在一个误区,包括我在内的好多开发者在刚接触这个关键字的时候都认为如果使用了volatile修饰变量则可以保证线程安全,这样理解其实是忽略了一个重要的问题,那就是java的运算符操作并不是原子的,虽然volatile保证了工作内存和主存的一致性,但是当执行j–时,j–会产生多个指令,这些指令执行时是非原子性的。所以执行上面类似的操作还是要使用synchronized等锁机制来保证原子性。

2、禁止指令重排序
指令重排序顾名思义就是将指令原本的执行顺序打乱。为什么要打乱呢,简单写一段伪代码,比如一段java程序。

private static void testVolatile2() {

    int i = 0;
    int j = 1;
    int ij = i + j; // 指令1
    int jxi = i*j;  // 指令2
}

比如上述代码中指令1和指令2谁先执行都可以,因为两个指令互不相干。实际上程序在运行时也会做类似的打乱指令去执行。但是如果把指令2修改为int ixj= i*j +ij, 那么很明显指令2要依赖指令1,这个时候如果发生了重排序则会导致程序异常。当然了,真实情况下计算机在重排序时要根据上下文依赖关系去重排序,不会发生以上这种情况。重排序的目的是为了减少cpu和内存的交互,尽可能保证cpu和寄存器或者高速缓存去交互。以上代码只是为了简单说明一下什么是指令重排序。

在《深入理解Java虚拟机:JVM高级特性与最佳实践》这本书中,特别提到一个大家都熟悉的案例,那就是使用双重检查锁来实现的单例模式,这个相信大家在业务代码或者框架中经常用到,如下:

public class Demo2 {

    private volatile static Demo2 demo2 = null;

    private void Demo2() {

    }

    public static Demo2 getInstance() {

        if (demo2 == null) {

            synchronized(Demo2.class) {
                if (demo2 == null) {
                    demo2 = new Demo2();
                }
            }
        }

        return demo2;
    }

}

如果代码中demo2对象不使用volatile修饰,那么很有可能出现因为指令重排序发生异常,因为demo2 = new Demo2()这句中的new并不是一个原子操作。实际上new Demo2的过程如下(借用网上的图):

image.png

如果线程 1 获取到锁进入创建对象实例,这个时候发生了指令重排序。当线程1 执行到 t3 时刻,线程 2 刚好进入,由于此时对象已经不为 Null,所以线程 2 可以自由访问该对象。然后该对象还未初始化,所以线程 2 访问时将会发生异常。

并且双重检测锁的还存在其他问题,该问题jdk1.5才被优化。

jvm内存模型中其他特殊的说明

在阅读《深入理解Java虚拟机:JVM高级特性与最佳实践》这本书时,在第12章12.3.4章节时,特别说明了long和double这两个特殊的变量,模型中特别定义了一条宽松的规定:允许虚拟机将没有 被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行这就导致了如果在多线程下不使用volatile修饰时,可能发生异常,因为可能读取到的“半个数据”。

附上原文:

image.png

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第27天,点击查看活动详情

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

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

相关文章

Phaser笔记-scene中的preload、create、update、player、键盘控制

一般phaser最简单的配置文件如下&#xff1a; let config {type: Phaser.AUTO,width: 800,height:600,scene: {preload: preload,create: create,update: update},physics:{default: arcade,arcade: {gravity: { y: 300},debug: false}}};其中scene有3个函数&#xff1a;prel…

CI570 3BSE001440R1适用于数字功能需求较多的设计

CI570 3BSE001440R1适用于数字功能需求较多的设计 尽管纯硅的CMOS 制程被认为仅适用于数字功能需求较多的设计&#xff0c;而不适用于以模拟电路为主的射频IC 设计&#xff0c;不过历经十几年的努力后&#xff0c;随着CMOS 性能的提升、晶圆代工厂在0.25mm 以下制程技术的配合、…

解决 Docker + selenium + chromedriver + chrome 会出现僵尸进程的问题

一、僵尸进程问题 在docker里&#xff0c;使用selenium爬虫&#xff0c; webdriver quit后&#xff0c;会产生很多僵尸进程。docker run -it -v /home/blackip:/home/blackips/ selenium:1.0python3 linux_black_ip.pytop查看僵尸进程&#xff1a;ps -ef | grep defunct查看…

微服务+springcloud+springcloud alibaba学习笔记【Ribbon的使用】(4/9)

Ribbon的使用 4/91、Ribbon负载均衡1.1 Ribbon简介1.2 Ribbon功能1.3 使用Ribbon:1.3.1 Ribbon常用负载均衡算法1.3.2 使用Ribbon1.3.3 ribbon的轮询算法原理1.3.4 手写一个负载均衡轮询算法1.3.5 启动服务,测试1、Ribbon负载均衡 1.1 Ribbon简介 Spring Cloud Ribbon是基于N…

Nestjs实战干货-概况-管道-Pipes

管道 带上装饰器 Injectable() 并实现了 PipeTransform 接口的类&#xff0c;就是管道。 管道有 2 个典型的应用场景&#xff1a; 数值转换&#xff1a;将输入的参数转换成目标类型&#xff0c;例如&#xff0c;string to number。 数值校验&#xff1a;对输入的参数进行校验…

记一次 MySQL 主从同步异常的排查记录,百转千回

本文主要内容如下&#xff1a; 一、现象 最近项目的测试环境遇到一个主备同步的问题&#xff1a; 备库的同步线程停止了&#xff0c;无法同步主库的数据更改。 备库报错如下&#xff1a; 完整的错误信息&#xff1a; Relay log read failure: Could not parse relay log even…

一文读懂【Git 工作流】

文章目录一、Git分支管理二、Git日志规范三、Git Flow工作流一、Git分支管理 我们在实际工作中会创建很多分支以便于不同场景下的开发&#xff0c;但是如果没有分支规范就会造成分支杂乱&#xff0c;大家往往也搞不清楚某一个分支是在做什么&#xff0c;下面我们就介绍一下我们…

车企围攻整车OS,这张“新王牌”怎么打?

今年2月23日&#xff0c;梅赛德斯--奔驰发布了打造自有操作系统MB.OS的具体计划&#xff0c;该操作系统将在本年代中期随全新梅赛德斯-奔驰模块化架构&#xff08;MMA&#xff09;平台推出&#xff0c;预计2025年用户将能体验到它的强大功能。 据悉&#xff0c;基于覆盖芯片到…

YOLOv8运行参数解读

整理来自yolov8官方文档常用的一些命令行参数&#xff0c;官方文档YOLOv8 Docs yolov8命令行的统一运行格式为&#xff1a; yolo TASK MODE ARGS其中主要是三部分传参&#xff1a; TASK(可选) 是[detect、segment、classification]中的一个。如果没有显式传递&#xff0c;YO…

智慧水务软件-科学系统架构-数字化管理

平台概述 柳林智慧水务软件是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心&#xff0c;以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑&#xff0c;以城市水资源、水生态、水环境…

VGG论文翻译及复现

VGG网络实现&#xff1a;https://blog.csdn.net/weixin_43912621/article/details/127852595 论文地址&#xff1a;https://arxiv.org/abs/1409.1556 VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION 用于大规模图像识别的深度卷积网络 Abstract In t…

Salesforce Admin管理员中文学习教程,如何高效筛选出具有Admin权限的用户!

组织中最常见的错误之一就是拥有太多具有系统管理员简档的用户。不幸的是&#xff0c;这在某些行业中非常普遍。 实际上这存在着很大的潜在风险。拥有这些权限的用户可能会暴露、窃取或删除组织中的数据&#xff0c;甚至影响到其他用户。防止过多的管理员访问权限是保护Salesf…

基于Python机器学习、深度学习技术提升气象、海洋、水文领域实践应用能力

目录 专题一、Python软件的安装及入门 专题二、气象常用科学计算库 专题三、气象海洋常用可视化库 专题四、爬虫和气象海洋数据 专题五、气象海洋常用插值方法 专题六、机器学习基础理论和实操 专题七、机器学习的应用实例 专题八、深度学习基础理论和实操 专题九、深…

摸鱼也可以效率翻倍:Python 统计 gitlab 代码量,定量统计发给领导

嗨害大家好鸭&#xff01;我是爱摸鱼的芝士❤ 一、确定需求 需求是公司大领导想要了解每周研发提交的代码量。 因为研发人员比较多&#xff0c; 想着用 python 做个自动化&#xff0c; 定时统计代码量并发送邮件给领导。 二、统计gitlab代码 首先安装第三方库python-gitlab&…

如何提升智能文档处理识别精度?合合信息“版面分析”实现新突破

春季是繁忙的播种季&#xff0c;学生党迎来了开学季和紧张的研究生复试&#xff0c;职场人士也需要处理新签业务带来的大量不同类型的文件&#xff0c;比如合同、发票、档案等。这些文件在被拍照、扫描成电子文档的过程中&#xff0c;时常存在漏字、错位现象。究其原因&#xf…

kali的下载与安装(VM虚拟机)

目录 一、介绍 二、下载安装 &#xff08;一&#xff09;官网下载kali &#xff08;二&#xff09;官网下载VM虚拟机 &#xff08;三&#xff09;安装VM虚拟机 &#xff08;三&#xff09;VM虚拟机里面安装kali系统 一、介绍 &#xff08;1&#xff09;Kali Linux是一种基…

基于Chatbot UI 实现ChatGPT对话-V1.0

基于Chatbot UI 实现ChatGPT对话-V1.0 前端基于开源项目&#xff1a;chatbot-ui进行二次开发&#xff0c;感兴趣的小伙伴可以自行研究。 本项目搭建初衷&#xff1a;在无法科学上网的情况下&#xff0c;实现ChatGPT对话。还有规避官方聊天时&#xff0c;长时间无链接导致的问题…

“GPT全家桶”,喂不饱商汤科技

加码追风大模型&#xff0c;终究没能让商汤科技找回投资者们的信心。 4月10日&#xff0c;商汤发布了“日日新SenseNova”大模型体系&#xff0c;且一口气展示了多个产品&#xff0c;有类ChatGPT产品“商量”&#xff08;SenseChat&#xff09;、与Midjourney画风一致的秒画平台…

语句覆盖率\条件覆盖率\路径覆盖率\分支覆盖率的区别您知道吗

代码覆盖率 代码覆盖率是一种度量&#xff0c;它描述了程序源代码已经过测试的程度&#xff0c;它可以帮助我们评估测试执行的效率&#xff0c; 简单来理解代码覆盖率就是单元测试中代码执行量与代码总量之间的比率。代码覆盖率主要包括语句覆盖率、分支覆盖率、条件覆盖率和路…

【Linux】实现守护进程 | 以tcpServer为例

本文首发于 慕雪的寒舍 本文将以tcp服务器代码为基本&#xff0c;讲述如何将进程守护进程化&#xff0c;后台运行 1.守护进程 所谓守护进程&#xff0c;就是和其他进程没有关系的进程&#xff1b;其独立运行于系统后台&#xff0c;除非自己退出或收到信号终止&#xff0c;否则…