JAVA并发编程学习笔记10-volatile

2023-11-12

概念

volatile关键字是由JVM提供的最轻量级的同步机制,它能保证内存可见性和防止指令重排序。

JMM(JAVA内存模型)常见概念

  • 原子性:保证指令不会受到上下文切换的影响
  • 有序性:保证指令不会受到CPU并行优化的影响
  • 可见性:保证指令不会受到CPU缓存的影响

可见性

在这里插入图片描述
多核CPU,由于CPU速度远大于内存速度,故在CPU和内存之间,存在缓存,可以一定程度降低两者之间的差距。但也因此出现了主存和缓存不一致的问题,这个问题我们称为可见性问题。

@Slf4j
public class Test {

    private static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (flag) {
            }
        }, "t1").start();

        Thread.sleep(1000);
        log.info("stop");
        flag = false;
    }
}
  • 期望结果:在主线程休眠结束后,由于flag变为false,while循环会结束。
  • 实际结果:t1仍旧处于死循环中,并未因为flag变为false而终止。
  • 原因分析:因为t1除了最初从主存中读取了flag的值之后,后续循环一直都读的缓存的值,当主线程更改了flag的值,并未通知t1缓存做出更改,导致循环未退出。
  • 解决方案:在flag前加上volatile关键字。因为加上volatile关键字后,当线程修改volatile所修饰的变量时,会直接将缓存中的值写入主存中,同时通知其他缓存更新主存中的数据,故当flag发生变化的同时,t1就监测到其变化,进而终止循环。

指令重排序

如下代码,CPU在保证最终结果一直的情况下,回对程序执行顺序进行优化,可能会先执行j = 2再执行i = 1;

int i = 1;
int j = 2;

说到指令重排序就不得不说到经典的dcl(double check locking)问题了

public class Singleton {

    private Singleton(){};

    private static Singleton INSTANCE = null;

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
  • 问题分析:上面代码是经典的dcl代码,但由于有指令重排序问题,可能会出现new Singleton()初始化时,先给INSTANCE绑定了对象地址,但对象还没来得及赋值的情况,而此时另一个线程拿到了这个未赋值的对象时,就会出问题。
  • 解决方法:给INSTANCE增加volatile修饰符
  • 原理分析:对volatile修饰的属性的读操作,会在读之前加上读屏障(防止读之后的代码跑到前面),对volatile修饰的属性的写操作,会在写操作之后加一个写屏障(防止写之前的操作跑后面去)。构造方法是在赋值前,故不会出现刚刚所说的结果。

happens-before规则

happens-before仅仅要求前一个操作的执行结果对后一个操作是可见的,且前一个操作按顺序排在后一个操作之前。

synchronized

@Slf4j
public class Test {

    private static int count = 0;

    private static Object lock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                count++;
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                log.info("count:{}", count);
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

volatile

@Slf4j
public class Test {

    private static volatile int count = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            count++;
        }, "t1");
        Thread t2 = new Thread(() -> {
            log.info("count:{}", count);
        }, "t2");
        t1.start();
        t2.start();
    }
}

Thread.start()方法

  • 调用start()方法前,需要用到的变量都是读可见
  • 同理,线程运行结束时,线程内做出的修改对其他线程读可见
@Slf4j
public class Test {

    public static void main(String[] args) {
        int count = 10;
        new Thread(() -> {
            log.info("count = {}", count);
        }, "t1").start();
    }
}

Thread.internupt()方法

线程被打断后,打断前的变量修改可见

@Slf4j
public class Test {

    private static int count = 10;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    log.info("count = {}", count);
                    break;
                }
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            count = 20;
            t1.interrupt();
        }, "t2");
        t1.start();
        t2.start();
        while (!t1.isInterrupted()) {
            Thread.yield();
        }
        log.info("count = {}", count);
    }
}

传递性

由于x被volatile修饰,x的修改是可见的,而y在x写操作之前,由于传递性y也是可见的

@Slf4j
public class Test {

    private static volatile int x = 0;

    private static int y = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            y = 10;
            x = 10;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            log.info("x = {}, y = {}", x, y);
        }, "t2");
        t1.start();
        t2.start();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA并发编程学习笔记10-volatile 的相关文章

  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • 如何默认将 Maven 插件附加到阶段?

    我有一个 Maven 插件应该在编译阶段运行 所以在项目中consumes我的插件 我必须做这样的事情
  • Java EE:如何获取我的应用程序的 URL?

    在 Java EE 中 如何动态检索应用程序的完整 URL 例如 如果 URL 是 localhost 8080 myapplication 我想要一个可以简单地将其作为字符串或其他形式返回给我的方法 我正在运行 GlassFish 作为应
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • 使用 JMF 创建 RTP 流时出现问题

    我正处于一个项目的早期阶段 需要使用 RTP 广播DataStream创建自MediaLocation 我正在遵循一些示例代码 该代码目前在rptManager initalize localAddress 出现错误 无法打开本地数据端口
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview

随机推荐

  • 【MyBatis】一天之内快速掌握MyBatis的增删改查

    MyBatis 文章介绍 一共不到8000字 一天之内可以快速学会mybatis的增删改查 以及经常使用的操作 不讲废话 只将眼光聚焦到实操上 也可以搭配黑马的视频观看 看完之后 可以自己找个mybatis源码课继续学习 但是一般的业务代码
  • Eclipse的安装与基本操作(详解配图)

    不为失败找理由 只为成功找方法 所有的不甘 都是因为还心存梦想 在你放弃之前 好好拼一把 只怕心老 不怕路长 文章目录 一 简介 二 下载 三 使用Eclipse编写第一个Java程序 四 Eclipse的基本操作 1 背景 2 字体 五
  • Windows10系统MySQL服务器简单安装

    Windows10系统MySQL服务器简单安装 Mysql下载 安装 修改MySQL数据保存地址 修改服务 初始化数据库 修改root密码 新增用户 Mysql下载 MySql官网下载 下载地址 https dev mysql com do
  • 创建M32F103C8T6的工程文件之后编译为什么一堆错误?

    首先先看错误的提示是不是以下的显示 Rebuild started Project STM32F103C8T6 Using Compiler V6 15 folder F keil ARM ARMCLANG Bin Rebuild targ
  • 关于vxe-table全局引入的问题

    主要讲解一下vxe table全局引入然后使用碰到的问题 0 vxe table的官网地址 1 基本环境 1 vue版本为3 x以上 我的是3 2 13 2 依赖库 xe utils 注意 这篇博客的是vue3的脚手架搭建的 如果需要看低版
  • 毕业设计-机器视觉的疲劳驾驶检测系统-python-opencv

    目录 前言 课题背景和意义 实现技术思路 实现效果图样例 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生级别难度
  • vue-admin-template,连接自己后台,二次开发必看

    第一步 找到 env development文件做如下修改 just a flag ENV development base api VUE APP BASE API api 第二步 找到 vue config js 配置跨域 关闭mock
  • Ubuntu 20版本将动态ip修改为静态ip时,ping 不通网络

    问题描述 在对Ubuntu 20版本将动态ip修改为静态ip时 ping www baidu com ping不通了 火狐浏览器没有了网路 下载不了东西 一直卡在这里不动 问题出在哪里还是配置ip dns 网关的问题 如果我们在当初安装ub
  • Spring:从零开始的Cloud生活(零)——Eureka 服务治理

    目录 Spring 从零开始的Cloud生活 零 Eureka 服务治理 1 Netfilx Eurake 2 搭建服务注册中心 3 服务提供者 4 高可用注册中心 5 服务发现和消费 之前对于SpringCloud都是一知半解的状态 现在
  • MySQL中的模糊查询

    1 表示任意0个或多个字符 可匹配任意类型和长度的字符 有些情况下若是中文 请使用两个百分号 表示 比如 SELECT FROM user WHERE u name LIKE 三 将会把u name为 张三 张猫三 三脚猫 唐三藏 等等有
  • python调用turtle(海龟画图),画一个正方形

    调用海龟画图 画一个正方形 方法一 调用海龟画图 import turtle bob turtle Turtle print bob 定义画图的方向 此处画了一个直角 bob fd 100 bob lt 90 加入以下步骤画了一个正方形 b
  • Linux Shell学习简单小结(更新中……)

    if fi bin bash 删除文件 和 新建文件 file readme function delFile if e file then rm f file echo del file fi function addFile if f
  • YOLO系列发展史

    YOLO You Only Look Once 是一种目标检测算法 由Joseph Redmon等人在2015年提出 它的主要思想是将目标检测任务看作是一个回归问题 并且可以在一个神经网络中同时预测目标的位置和类别 自2015年YOLO第一
  • ChatGPT会取代互联网程序员吗?

    ChatGPT会取代互联网程序员吗 ChatGPT是一个基于GPT 3模型的自然语言对话系统 它可以与用户进行自然 流畅 智能的对话交互 回答用户的问题 提供用户所需的信息 甚至生成一些有趣和创意的内容 ChatGPT最近火爆全网 一时间C
  • Linux系统启动分析

    文章目录 大体流程分析 一 BIOS 1 1 BIOS简介 1 2 POST 二 BootLoader GRUB 2 1 What s MBR 2 2 What s GRUB 2 3 boot img 2 4 core img lzma d
  • 我花了一夜用数据结构给女朋友写个H5走迷宫游戏

    文章目录 起因 分析 画线 棋盘 画迷宫 方块移动 结语 先看效果图 在线电脑尝试地址http biggsai com maze html 起因 又到深夜了 我按照以往在公众号写着数据结构 这占用了我大量的时间 我的超越妹妹严重缺乏陪伴而
  • Vue-数据驱动视图(一)数据监听机制

    前言 Vue的一大特点就是数据双向绑定 当数据发生变化时 也可以同时触发界面的变化 即数据驱动视图 要想实现数据驱动视图 那么有这么几个步骤 1 收集我们需要监听的数据 并给他配置个监听器 当数据状态发生变化时触发监听器 然后判断是否需要重
  • Echarts地图自定义图标Symbol同时动态更改图标进行切换显示

    Echarts地图自定义图标Symbol同时动态更改图标进行切换显示 文章目录 0 前言 1 引用模块 2 界面设计 3 数据准备 4 实例地图 5 动态修改气泡图标 6 结束语 0 前言 实现的效果 实现广东显示地图 然后点击地图上的气泡
  • MATLAB数据曲线拟合

    MATLAB数据曲线拟合 数据拟合是我们常用的一种方法 可以通过一组离散的数据点来找到一个函数 使这个函数能够对数据进行预测和描绘 在MATLAB中实现数据拟合非常简单 而且MATLAB还提供了许多工具箱来帮助我们完成这项任务 下面我们将会
  • JAVA并发编程学习笔记10-volatile

    JAVA并发编程学习笔记10 volatile 概念 JMM JAVA内存模型 常见概念 可见性 指令重排序 happens before规则 synchronized volatile Thread start 方法 Thread int