java JUC并发编程 第五章 volatile与JMM

news2024/12/24 2:29:17

系列文章目录

第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link


文章目录

  • 系列文章目录
  • 1 volatile 2大特点
  • 2 volatile 内存屏障
    • 2.1 内存屏障的分类
      • 2.1.1粗分2种
      • 2.1.2 细分4种
      • 2.2.3 什么叫保证有序性?
      • 2.2.4 happens-before之volatile变量规则
      • 2.2.5 lolatile读插入内存屏障后生成的指定序列示意图
      • 2.2.6 volatile写插入内存屏障后生成的指令序列示意图
  • 3 volatile特性
    • 3.1 保证可见性
      • 3.1.1 不加volatile,没有可见性,程序无法停止
      • 3.1.2 加了volatile,保证可见性,程序可以停止
      • 3.1.3 volatile变量的读写过程
    • 3.2 没有原子性
      • 3.2.1 加了synchronized关键字保证在多线程环境下的原子性
      • 3.2.2 volatile不具备原子性
    • 3.3 指令禁重排
    • 3.4 正确使用volatile
  • 4 总结
    • 4.1 volatile可见性
    • 4.2 volatile没有原子性
    • 4.3 volatile禁重排
      • 4.3.1 写指令
      • 4.3.2 读指令
    • 4.4 java写了一个volatile关键字系统底层加入内存屏障,两者关系的关系
    • 4.5 内存屏障是什么
    • 4.6内存屏障能干嘛
    • 4.7内存屏障四大指令
    • 4.8 3句话总结


1 volatile 2大特点

特点,可见性,有序性,没有原子性。排序要求禁止指令重排
内存语意
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,重新回到主内存中读取最新共享变量
所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接重主内存中读取
volatile靠内存屏障Memory Barrier保证可见性和有序性。

2 volatile 内存屏障

在这里插入图片描述
在这里插入图片描述

2.1 内存屏障的分类

2.1.1粗分2种

1.读屏障(Load Barrier):在读指令之前插入读屏障,让工作内存或CPU高速缓存当中的缓存数据失效,重新回到主内存中获取最新数据
2.写屏障(Store Barrier):在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中

2.1.2 细分4种

loadload(),storestore(),loadstore(),storeload()
在这里插入图片描述

C++源码分析
Unsafe.class
在这里插入图片描述
Unsafe.cpp
在这里插入图片描述
orderAccess.hpp
在这里插入图片描述
orderAccess_linux_x86.inline.hpp
在这里插入图片描述

2.2.3 什么叫保证有序性?

禁重排,通过内存屏障禁重排
1.重排序有可能影响程序的执行和实现,因此,我们有时候希望告诉JVM不能重排序,
2.对于编译器的重排序,JMM会根据重排序的规则,禁止特定类型的编译器重排序。
3.对于处理器的重排序,Java变意思在生成指令序列的适当位置,插入内存屏障指令,来禁止特定类型的处理器排序

2.2.4 happens-before之volatile变量规则

在这里插入图片描述

2.2.5 lolatile读插入内存屏障后生成的指定序列示意图

在这里插入图片描述

2.2.6 volatile写插入内存屏障后生成的指令序列示意图

在这里插入图片描述

3 volatile特性

3.1 保证可见性

保证不通线程对摸个变量完成操作后结果及时可见,即改共享变量一旦改变所有线程立即可见

3.1.1 不加volatile,没有可见性,程序无法停止

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

public class VolatileSeeDemo {
    static  boolean flag = true;
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"----come in");
            while(flag){
            }
            System.out.println(Thread.currentThread().getName()+"----flag 为false");
        },"t1").start();
        try {TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e) {e.printStackTrace();}
        flag=false;
        System.out.println(Thread.currentThread().getName()+"修改完成");
    }
}

执行结果:程序无法完成
在这里插入图片描述

3.1.2 加了volatile,保证可见性,程序可以停止

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

public class VolatileSeeDemo {
//    static  boolean flag = true;
    static volatile  boolean flag = true;
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"----come in");
            while(flag){
            }
            System.out.println(Thread.currentThread().getName()+"----flag 为false");
        },"t1").start();
        try {TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e) {e.printStackTrace();}
        flag=false;
        System.out.println(Thread.currentThread().getName()+"修改完成");
    }
}

执行结果:程序正常结束
在这里插入图片描述
总结:
线程t1为何看不到被主线程main修改为false的flag的值?
1.主线程修改了flag之后没有将其刷新到主内存,所以t1线程看不到
2.主线程将flag刷新到了主内存,但是t1一直读取的是自己工作内存中的flag的值,没有取主内存中更新获取flag最新的值
诉求:
1.线程中修改了自己工作内存中的副本之后,立即将其刷新到主内存。
2.工作内存中每次读取共享变量时,都去主内存中重新读取,然后拷贝到工作内存。
解决:
使用volatile修饰共享变量,就可以达到上面的效果,被volatile修改的变量有以下特点
1.线程中读取的时候,每次读取都会去主内存中读取共享变量最新的值,然后将其复制到工作内存
2.线程中修改了工作内存中变量的副本,修改之后会立即刷新到主内存。

3.1.3 volatile变量的读写过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2 没有原子性

3.2.1 加了synchronized关键字保证在多线程环境下的原子性

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

class  MyNumber{
    int number;
    public synchronized void addPlusPlus(){
        number++;
    }
}
public class VolatileNoAtomicDemo {
    public static void main(String[] args) {
        MyNumber myNumber = new MyNumber();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    myNumber.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        try {TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e) {e.printStackTrace();}
        System.out.println(myNumber.number);
    }
}

3.2.2 volatile不具备原子性

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;

class  MyNumber{
    volatile int number;
    public void addPlusPlus(){
        number++;
    }
}
public class VolatileNoAtomicDemo {
    public static void main(String[] args) {
        MyNumber myNumber = new MyNumber();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    myNumber.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        try {TimeUnit.SECONDS.sleep(1);}catch (InterruptedException e) {e.printStackTrace();}
        System.out.println(myNumber.number);
    }
}

多次运行后其中一次结果与预期不一样
在这里插入图片描述
为什么没有原子性?
读取赋值一个普通变量的情况
在这里插入图片描述
i++在java中的操作分为3步1.数据加载,2数据计算,3数据赋值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 指令禁重排

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.4 正确使用volatile

单一赋值可以(volatile int a =10; bolatile boolean flag=false),但是含复合运算赋值不可以(i++之类)
状态标志,判断业务是否结束
在这里插入图片描述

开销较低的读,写锁策略
在这里插入图片描述

DCL双端锁的发布

在这里插入图片描述
在这里插入图片描述
存在指令重排序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 总结

4.1 volatile可见性

在这里插入图片描述

4.2 volatile没有原子性

4.3 volatile禁重排

4.3.1 写指令

在这里插入图片描述
在这里插入图片描述

4.3.2 读指令

在这里插入图片描述
在这里插入图片描述

4.4 java写了一个volatile关键字系统底层加入内存屏障,两者关系的关系

在这里插入图片描述

4.5 内存屏障是什么

内存屏障:是一种 屏障指令,它使得CPU或编译器对屏障指令的前和后所发出的内存操作,执行一个排序的约束。也叫内存栅栏或栅栏指令

4.6内存屏障能干嘛

阻止屏障两边的指令重排序
写数据时加入屏障,强制将线程私有工作内存的数据刷回主物理内存
读数据时加入屏障,线程私有工作内存的数据失效,重新到主物理内存中获取最新数据

4.7内存屏障四大指令

在每一个volatile写操作前面插入一个StoreStore屏障
在每一个volatile写操作后面插入一个StoreLoad屏障
在每一个volatile读操作后面插入一个LoadLoad屏障
在每一个volatile读操作后面插入一个LoadStore屏障

4.8 3句话总结

volatile写之前的操作,都禁止重排序到volatile之后
volatile读之后的操作,都禁止重排序到volatile之前
volatile写之后volatile读,禁止重排序

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

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

相关文章

KVM虚拟化ubuntu

KVM&#xff08;Kernel-based Virtual Machine&#xff09;是一种基于Linux内核的虚拟化技术&#xff0c;它将Linux内核作为虚拟机的底层操作系统&#xff0c;利用硬件虚拟化支持创建和管理虚拟机。KVM虚拟化技术被广泛应用于云计算、虚拟化服务器、虚拟化桌面等场景。 KVM虚拟…

最新无代码排名出炉,哪个平台最适合你?

随着无代码技术的迅速发展&#xff0c;国内外涌现出许多优秀的无代码平台提供商&#xff0c;企业在选择合适的无代码平台时可能会感到困惑&#xff0c;无从下手。为了帮助大家更好地了解国内真正的无代码平台厂商&#xff0c;本文将为您介绍几家具有代表性的厂商。 1.云表平台&…

数组中的第K个最大元素

题目链接 数组中的第K个最大元素 题目描述 注意点 需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素 解答思路 本题可以使用快速排序、堆排序或优先队列解决&#xff0c;快排可以比较快速找到某个元素在数组中排序后的位置&#xff0c;所以找…

Redis——》Redis的部署方式对分布式锁的影响

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…

ToBeWritten之针对 Car ATTCK 的攻防矩阵

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

有时间窗车辆路径问题(vehicle routing problems with time windows,VRPTW)学习实践与base案例代码开发

有时间窗车辆路径问题&#xff08;Vehicle Routing Problems with Time Windows&#xff0c;VRPTW&#xff09;是一类著名的组合优化问题&#xff0c;涉及在有限时间窗口约束下&#xff0c;有效地安排多个车辆的路径&#xff0c;以满足客户需求。 在VRPTW中&#xff0c;假设有…

what(): NCCL Error 1: unhandled cuda error解决方法

文章目录 遇到问题解决方法参考 遇到问题 运行项目&#xff1a;ACL2021的一篇工作&#xff0c;LM-BFF (Better Few-shot Fine-tuning of Language Models) https://github.com/princeton-nlp/LM-BFF 遇到环境问题。 我的机器环境如下&#xff1a; 服务器上CUDA版本为11.4 GPU…

为什么要参加浙大MPA提面申请?你看看AB资格的弃考率

时间即将来到九月份&#xff0c;在这个时间点依然能够坚持自己读研梦的考生&#xff0c;可以说基本是今年真正的准考生了&#xff01;在备考坚持这方面&#xff0c;其实每年很多考生走不到最后&#xff0c;比如在9-10月份全国研究生网上报名阶段&#xff0c;接近两成比例的考生…

geant4 常用代码

1 获取特特定能量范围的特定粒子 E:\examples_understanding\geant4-v11.0.0_note\examples\extended\runAndEvent\RE02 //-- Particle with kinetic energy filter.G4SDParticleWithEnergyFilter* pkinEFilter new G4SDParticleWithEnergyFilter(fltName"gammaE filter&…

招生大户!包邮区双非计算机强校计算机考研分析

杭州电子科技大学(B) 考研难度&#xff08;☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23初试科目、23复试详情、各专业考情分析、各科目考情分析。 正文1689字&#xff0c;多表&#xff0c;预计阅读&#xff1a;5分钟…

Python中小数据池知识最详细教程

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 一、id&#xff0c;is&#xff0c; 在Python中&#xff0c;id是什么&#xff1f; id是内存地址&#xff0c;比如你利用i…

centos升级python

升级为python3.10 yum updateyum install openssl-devel bzip2-devel libffi-develyum groupinstall "Development Tools"wget https://www.python.org/ftp/python/3.10.2/Python-3.10.2.tgztar -xzf Python-3.10.2.tgzcd Python-3.10.2./configure --enable-optimi…

JavaScript常见的运算符

一、运算符和运算元 1.认识运算符 在小学的时候我们就学习了各种运算符&#xff0c;比如加号 、乘号 *、减号 - 、除号/ 几乎所有的编程语言都有各种各样的运算符(也被称之为操作符&#xff0c;operators) 初次接触这些运算符, 你会感觉种类繁多, 难以记忆.但是并不需要特别…

Lora升级!ReLoRa!最新论文 High-Rank Training Through Low-Rank Updates

目录 摘要1 引言2 相关工作3 方法4 实验5 结果6 结论7 局限性和未来工作 关注公众号TechLead&#xff0c;分享AI与云服务技术的全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0…

使用文本编辑器sublime text编写python代码

前言 上篇文章介绍了如何安装python程序&#xff0c;这次就讲讲如何用sublime来编写python代码 有的小伙伴可能会有疑问&#xff0c;为什么不选择pycharm来编写&#xff1f; pycharm写代码会有代码补全功能&#xff0c;而我们新手正是要在犯错误的过程中积累经验&#xff0c…

论文解读 | 新发现编码器PointPillars用于点云检测物体,更加高效且精准

原创 | 文 BFT机器人 01 技术背景 在过去的几年中&#xff0c;深度学习技术已经在图像领域的物体检测中取得了显著的进展&#xff0c;诸如目标检测算法&#xff08;如Faster R-CNN、YOLO和SSD&#xff09;在图像中能够高效准确地检测出物体。然而&#xff0c;当涉及到点云数据…

get√接口自动化核心知识点浓缩,为面试加分

日常接触到的接口自动化从实际目标可以划分为两大类&#xff1a; 1、为模拟测试数据而开展的接口自动化 这种接口自动化大多是单次执行&#xff0c;目的很明确是为了功能测试创造测试数据&#xff0c;节约人工造数据的时间和人工成本&#xff0c;提高功能测试人员的测试效率。…

《论文阅读18》JoKDNet

一、论文 研究领域&#xff1a;用于大尺度室外TLS点云配准的联合关键点检测和特征表达网络论文&#xff1a;JoKDNet: A joint keypoint detection and description network for large-scale outdoor TLS point clouds registration International Journal of Applied Earth Ob…

Java定时任务调用执行shell脚本实例

文章目录 Java代码1234 shell脚本 Java代码 1 2 3 4 shell脚本

检查nacos是否正常启动

可以通过以下几种方式检查nacos是否启动&#xff1a; 查看nacos日志文件&#xff1a;启动nacos时&#xff0c;控制台会输出一些信息&#xff0c;可以查看控制台输出的信息&#xff0c;如果显示“Server startup in XXX ms”等字样&#xff0c;则说明nacos已经启动。 查看端口…