Java 多线程之 volatile(可见性/重排序)

文章目录

    • 一、概述
    • 二、使用方法
    • 三、测试程序
      • 3.1 验证可见性的示例
      • 3.2 验证指令重排序的示例

一、概述

  • 在Java中,volatile 关键字用于修饰变量,其作用是确保多个线程之间对该变量的可见性禁止指令重排序优化

  • 当一个变量被声明为volatile时,线程在读取和写入该变量时会直接操作主内存中的值,而不会使用线程自己的工作内存。这意味着当一个线程修改了一个volatile变量的值时,其他线程将立即看到这个变化,而不会使用缓存中的旧值。底层原理应该是实现了多CPU缓存一致性协议(如MESI),保证了线程可见性。如下图

    在这里插入图片描述

  • volatile关键字还可以防止指令重排序优化。在多线程环境下,为了提高执行效率,编译器和处理器可能会对指令进行重排序。然而,这种重排序可能会导致多线程程序出现意想不到的结果。通过使用volatile关键字,可以确保特定操作的执行顺序与程序中的顺序一致,从而避免了指令重排序可能引发的问题。底层原理应该是使用了屏障(如loadfence、storefence原语指令),禁止指令重排序。

  • volatile 关键字只能保证单个变量的原子性操作和可见性,并不能替代synchronized关键字或Lock接口来实现更复杂的操作。如果需要进行复合操作,例如原子性的读取-修改-写入操作,仍然需要使用synchronized关键字或Lock接口来保证线程安全性。如 x = y++; 则需要使用 synchronized 将整个语句加锁。

二、使用方法

  • 使用时方法简单,直接在变量定义时添加 volatile 关键字即可,如下

    volatile int count1 = 1;
    private volatile int count2 = 2;
    volatile boolean flag1 = false;
    private volatile boolean flag2 = false;
    

三、测试程序

3.1 验证可见性的示例

  • 在下面示例中,Counter 类有一个 count 变量用于计数,如果不使用 volatile 关键字修饰。在 increment 方法中,两个线程分别对 count 进行自增操作。然后在 Main 类的 main 方法中,创建了两个线程并启动它们,每个线程分别对 Counter 对象的 count 执行1000次自增操作。

  • 由于没有使用 volatile 关键字,线程在执行自增操作时,会将 count 的值从主内存复制到各自的线程工作内存中,进行自增操作后再将结果写回主内存。这可能导致一个线程对 count 的修改无法被另一个线程立即感知到,从而导致计数不准确。

  • 因此,当运行示例时,输出的最终计数结果可能小于2000,因为两个线程之间的自增操作并没有得到正确同步和可见性保证。

  • 相反,如果给 count 变量上添加 volatile 关键字修饰符,可以确保线程之间对该变量的读写操作具有可见性和一致性,从而解决问题。

    package top.yiqifu.study.p004_thread;
    
    
    public class Test051_VolatileVisible {
    
        public static class Counter {
            // 不使用 volatile 关键字
            private int count = 0;
    
            // 使用 volatile 关键字
            // private volatile int count = 0;
    
    
            public void increment() {
                count++;
            }
    
            public int getCount() {
                return count;
            }
        }
    
        public static void main(String[] args) {
            Counter counter = new Counter();
    
            Thread thread1 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    counter.increment();
                }
            });
    
            Thread thread2 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    counter.increment();
                }
            });
    
            thread1.start();
            thread2.start();
    
            try {
                thread1.join();
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("最终结果: " + counter.getCount());
        }
    
    }
    
    

3.2 验证指令重排序的示例

  • 在下面示例中,Test052_VolatileReorderingExample 类有一个 writer 方法和一个 reader 方法。

    • 在 writer 方法中,首先对变量 x 赋值为1,然后将 flag 设置为 true。
    • 在 reader 方法中,如果 flag 的值为 true,则打印变量 x 的值。
  • 创建一个测试方法 test,在这个方法中创建了两个线程,一个线程执行 writer 方法,另一个线程执行 reader 方法。然后在 main 方法中,创建一个线程用死循环去执行他。

  • 由于变量x和flag没有使用 volatile 关键字,编译器和处理器可能会对指令进行重排序。在不保证顺序性的情况下,可能会发生以下两种重排序情况:

    • 写入操作的重排序:编译器和处理器可能会将写操作2(flag = true)重排序到写操作1(x = 1)之前。
    • 读取操作的重排序:编译器和处理器可能会将读操作2(int a = x)重排序到读操作1(if (flag))之前。
  • 这种重排序可能导致在 reader 方法中打印的变量 a 的值为0,即使在 writer 方法中已经将其设置为1。这是因为在没有足够同步保证的情况下,读操作可能先于写操作执行。

  • 要解决这个问题,可以通过在 x 和 flag 变量上添加 volatile 关键字修饰符,可以防止指令重排序,从而避免这种问题。

  • 下面是测试程序,我在测试时执行了35万次时出现了指令重排序,出现这个问题的概念不是固定的,您测试时需要耐心等待。

    • Test052_VolatileReorderingExample.java 文件内容

      package top.yiqifu.study.p004_thread;
      
      public class Test052_VolatileReorderingExample {
      
          // 不使用 volatile 关键字
          private int x = 0;
          private boolean flag = false;
      
      //        // 使用 volatile 关键字
      //        private volatile int x = 0;
      //        private volatile boolean flag = false;
      
          public void writer() {
              x = 1;          // 写操作1
              flag = true;    // 写操作2
          }
      
          public void reader() {
              if (flag) {     // 读操作1
                  int a = x;  // 读操作2
                  if(a == 0) {
                      System.out.println("出现了指令重排序,说明先执行了 flag = true,  x = 1 还没有执行");
                  }
              }
          }
      
      }
      
      
    • 测试类 Test052_VolatileOrder.java 内容

      package top.yiqifu.study.p004_thread;
      
      public class Test052_VolatileOrder {
      
          private static void test(){
              Test052_VolatileReorderingExample example = new Test052_VolatileReorderingExample();
      
              Thread thread1 = new Thread(() -> {
                  example.writer();
              });
      
              Thread thread2 = new Thread(() -> {
                  example.reader();
              });
      
              thread1.start();
              thread2.start();
      
              try {
                  thread1.join();
                  thread2.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      
          public static void main(String[] args) {
      
              Thread thread = new Thread(()->{
                  long count = 0;
                  while (true){
                      test();
                      Thread.yield();
                      count++;
                      if(count%10000 == 0){
                          System.out.println("主线程还活着,已执行"+count+"次");
                      }
                  }
              });
              thread.start();
      
              try {
                  thread.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      
      }
      
      

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Vue+SpringBoot的校园电商物流云平台开源项目

项目编号&#xff1a; S 034 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S034&#xff0c;文末获取源码。} 项目编号&#xff1a;S034&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 商品数据模块2.3 快…

企业怎么进行人事管理?一篇文章带你了解!

阅读本文你将了解企业如何运用数字化工具进行人事管理&#xff1a;一、数字化、线上化&#xff0c;解放人力&#xff1b;二、规范管理流程&#xff0c;提升处理效率&#xff1b;三、数据分析可视化&#xff0c;支持并优化决策&#xff1b;四、个性化定制&#xff0c;灵活适应需…

mac 和 windows 相互传输文件【共享文件夹】

文章目录 前言创建共享文件夹mac 连接共享文件夹 前言 温馨提示&#xff1a;mac 电脑和 windows 电脑必须处于同一局域网下 本文根据创建共享文件夹的方式实现文件互相传输&#xff0c;所以两台电脑必须处于同一网络 windows 创建共享文件夹&#xff0c;mac 电脑通过 windows…

Enterprise Architect安装与使用

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Enterprise Architect概述 官方网站&#xff1a;https://www.sparxsystems.cn/products/ea/&#xff1b;图示如下&#xff1a; Enterprise Architect是一个全功能的、基于…

Spring-IOC-FactoryBean机制(难点且重点)

1、第一个案例 1.1、Book.java package com.atguigu.ioc; import lombok.Data; Data public class Book {private String bid;private String bname; }1.2、Book2.java package com.atguigu.ioc; import lombok.Data; Data public class Book2 extends Book {private String co…

SocketIo的使用和基于SocketIO的聊天室

Socket.IO 是一个库&#xff0c;可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信。 一、Socket.IO的特点 以下是 Socket.IO 在普通 WebSockets 上提供的功能&#xff1a; 1、HTTP 长轮询回退 如果无法建立 WebSocket 连接&#xff0c;连接将回退到 HTTP 长轮…

文化传承与数字技术的完美结合:十八数藏的新纪元

在这数字化的时代&#xff0c;十八数藏犹如一座连接过去与未来的桥梁&#xff0c;展现出文化传承与数字技术完美结合的新纪元。十八数藏以其独特的视角&#xff0c;将传统文化注入现代数字技术的脉络&#xff0c;呈现出一幅文化传承的全新画卷。 十八数藏的文化传承并不是简单的…

C++进阶篇5-哈希

一、unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到log_2N&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最优的查询是&#xff0…

Linux—简介安装常用命令系统中软件安装项目部署

目录 1. 前言1.1 什么是Linux1.2 为什么要学Linux1.3 学完Linux能干什么 2. Linux简介2.1 主流操作系统2.2 Linux发展历史2.3 Linux系统版本 3. Linux安装3.1 安装方式介绍3.2 安装VMware3.3 安装Linux3.4 网卡设置3.5 安装SSH连接工具3.5.1 SSH连接工具介绍3.5.2 FinalShell安…

leetcode:1773. 统计匹配检索规则的物品数量(python3解法)

难度&#xff1a;简单 给你一个数组 items &#xff0c;其中 items[i] [typei, colori, namei] &#xff0c;描述第 i 件物品的类型、颜色以及名称。 另给你一条由两个字符串 ruleKey 和 ruleValue 表示的检索规则。 如果第 i 件物品能满足下述条件之一&#xff0c;则认为该物…

Softing TCS:高效的诊断模拟解决方案

| Softing TCS——高效的仿真模拟ECU或整车解决方案 现代诊断模拟Softing TCS是当相应的测试物还没有或不再可用时的解决方案。这种情况通常出现在早期阶段的组件和车辆工程、测试仪回归测试中或在对教学设施进行功能验证时&#xff0c;因为它们需要对多种不同的测试对象进行验…

Python之pyc文件的生成与反编译

目录 1、什么是pyc文件 2、手动生成pyc文件 3、pyc文件的执行 4、pyc文件的反编译 1、什么是pyc文件 pyc文件&#xff08;PyCodeObject&#xff09;是Python编译后的结果。当python程序运行时&#xff0c;编译的结果是保存于PyCodeObject&#xff0c;程序运行结束后&#x…

linux基本指令以及热键

基本指令 ♥clear ♥whoami ♥who ♥pwd ♥ls指令&#xff08;重点&#xff09; ls -a&#xff1a; ls -l ♥mkdir ♥cd指令 ♥touch指令 ♥stat指令 ♥rmdir指令 && rm 指令 ♥man指令 ♥nano指令 ♥cp指令 ♥mv指令 ♥cat指令 &#x1f5e1;输出/输出重定向 &#x1…

速锐得解码匹配驾培驾考吉利几何E萤火虫数据应用智能评判系统

随着国内新能源车的不断发展和渗透&#xff0c;在驾培驾考领域通过新能源车进入到驾驶员培训领域的车型越来越多&#xff0c;这里边包括了特斯拉、宝马、通用、沃尔沃、岚图、江淮、蔚来、比亚迪、吉利、奇瑞、大众等多家车企的车型。 之前我们做过像奇瑞艾瑞泽、江淮IEV7、大…

大华智能物联综合管理平台readpic接口任意文件读取漏洞复现 [附POC]

文章目录 大华智能物联综合管理平台readpic接口任意文件读取漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 大华智能物联综合管理平台readpic接口任意文件读取漏洞复现 [附POC] 0x01 前言 免责…

微信小程序知识付费平台,公众号App+SAAS+讲师端,多端部署

三勾知识付费系统基于thinkphp8element-plusuniapp打造的面向开发的知识付费系统&#xff0c;方便二次开发或直接使用&#xff0c;可发布到多端&#xff0c;包括微信小程序、微信公众号、QQ小程序、支付宝小程序、字节跳动小程序、百度小程序、android端、ios端。 功能包含直播…

2022最新版-李宏毅机器学习深度学习课程-P49 GPT的野望

GPT→类似于Transformer Encoder 训练任务&#xff1a;Predict Next Token 使用MASK-attention&#xff0c;不断预测“下一个token”。 可以用GPT生成文章。 How to use GPT? 给出描述和例子 给出前半段&#xff0c;补上后半段 In-context Learning(no GD) 结果 目前看起…

竞赛 题目:基于深度学习的图像风格迁移 - [ 卷积神经网络 机器视觉 ]

文章目录 0 简介1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习卷积神经网络的花卉识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c…

初步设计报告(框架)

前言 &#xff08;简述初步设计报告编制的背景、过程及其它有关情况&#xff09;&#xff08;内容可根据项目的实际建设内容进行选择&#xff09; 编制单位资质证明

el-checkbox 对勾颜色调整

对勾默认是白色 改的时候一直在试着改color人&#xff0c;其实不对。我用的是element ui 的复选框 /* 对勾颜色调整 */ .el-checkbox__inner::after{/* 是改这里的颜色 */border: 2px solid #1F7DFD; border-left: 0;border-top: 0;}