Java并发编程(二)并发理论[JMM/重排序/内存屏障/Happens-Before 规则]

news2024/10/7 14:25:33

JMM(Java内存模型)

概述

  • JMM即Java内存模型(Java Memory Model),是一种抽象的概念,并不真实存在,JMM描述的是一组规则或规范,通过这组规范定义了程序中各个变量的访问方式
  • Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作必须在工作内存中进行,所以首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量
  • JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存,用于存储线程私有的数据
  • 工作内存中存储着主内存中的变量副本拷贝,不同的线程间无法访问对方的工作内存,线程间的通信必须通过主内存来完成

JMM规范

 JMM与JVM内存区域都存在共享数据区和私有数据区的概念

  • 在JMM中主内存属于共享数据区,从某个程度上讲应该包括了堆和方法区
  • 在JMM中工作内存属于线程私有数据区,从某个程度上讲则应该包括程序计数器、虚拟机栈以及本地方法栈

JMM与硬件内存架构的关系

从上图可以得出JMM和计算机硬件内存架构是一个相互交叉的关系,是一种抽象概念划分与真实物理硬件的交叉。最终多线程的执行最终都会映射到硬件处理器上进行执行,Java内存模型和硬件内存架构并不完全一致,JMM对内存的划分对硬件内存并没有任何影响,不管是工作内存的数据还是主内存的数据,对于计算机硬件来说,在计算机主内存、CPU缓存或者寄存器中,都可能存在

  • 硬件内存:分为寄存器、CPU缓存、主内存
  • JMM:分为工作内存(线程私有数据区域)和主内存(堆内存)

JMM作用

多个线程同时对一个主内存中的实例对象的变量进行操作有可能导致线程安全问题,所以需要JMM保证主内存与工作内存间数据一致

 从上图可以得出如下信息

  • CPU的运行并不是直接操作内存而是先把内存里边的数据读到缓存,而内存的读和写操作在并发场景下就会造成不一致的问题。所以JVM规范中定义一种Java内存模型(java Memory Model,简称JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果;
  • JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必要时如何同步的访问共享变量,实现了线程和主内存之间的抽象关系

JMM内存交互操作

八种内存交互操作

JMM内存交互协议指的是主内存与工作内存之间的交互协议,JMM定义了八种内存交互操作来定义一个变量应该如何从主内存拷贝到工作内存、如何从工作内存同步到主内存。

主内存和工作内存同步过程

主内存和工作内存同步变量值规则分析

  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或者assign)的变量。即就是对一个变量实施use和store操作之前,必须先自行assign和load操作。
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现。
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量之前需要重新执行load或assign操作初始化变量的值。
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)

JMM如何解决可见性,有序性,原子性问题

在多线程场景下,由于多线程情况复杂,为了保证每个线程能看到正确的结果,所以必须要保证线程的可见性,有序性与原子性

  • 可见性问题
    • volatile关键字保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值立即被其他的线程看到,即修改的值立即更新到主存中,当其他线程需要读取时,它会去内存中读取新值
    • synchronized和Lock也可以保证可见性,因为它们可以保证任一时刻只有一个线程能访问共享资源,并在其释放锁之前将修改的变量刷新到内存中
  • 有序性问题
    • volatile关键字可以保证“一定程度上的有序性”,一定程度上禁止指令重排
    • synchronized和Lock也可以保证“一定程度上的有序性”,不过与volitile的效果不同,synchronized和Lock保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性
  • 原子性问题
    • JDK提供的对基本数据类型读写操作的原子性,如AtomicInteger类等
    • synchronized和Lock实现原子性。因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块

重排序

概述

重排序是编译器和处理器为了优化程序性能而对指令序列进行重排序,从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序:

  • 编译器重排序的典型就是通过调整指令顺序,在不改变程序语义的前提下,尽可能减少寄存器的读取、存储次数,降低了重复读取的开销,充分复用寄存器的存储值
  • 指令集并行的重排序是对CPU的性能优化,通过重排尽可能阻止流水线技术中断,提升CPU执行性能
  • 内存系统重排序可以通过伪重排序减少CPU与主内存交互时CPU的短暂卡顿,从而提升性能(但延时写入主内存可能会导致数据不一致) 

数据依赖性

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性,数据依赖性分为下列三种类型

上述三种情况下只要重排序两个操作的执行顺序,程序的执行结果就会改变。编译器和处理器在重排序时,会遵循数据依赖性,编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序

as-if-serial语义

不管怎么重排序(编译器为了提高并行度),(单线程)程序的执行结果不能被改变.编译器、runtime和处理器都必须遵守as-if-serial语义

重排序原则

内存屏障

内存屏障是为了禁止编译器重排序和CPU重排序,内存屏障在编译器和CPU层面上都有对应的指令(其中CPU的内存屏障是CPU提供的指令,可以由开发者显示调用)

Happens-Before 规则

  • as-if-serial语义保证单线程内程序的执行结果不会被改变,happens-before具有传递性关系,保证正确同步的多线程程序的执行结果不会被改变。且两者的目的都是为了在不改变程序执行结果的前提下,尽可能的提高程序执行的并行度
  • 程序顺序规则:在一个线程内一段代码的执行结果是有序的(在单线程情况下,对不存在数据依赖性的指令进行重排序,只保证单线程执行结果的正确性,不保证程序在多线程中执行的正确性)
  • 监视器锁规则:就是无论是在单线程环境还是多线程环境,对于同一个锁来说,一个线程对这个锁解锁之后,另一个线程获取了这个锁都能看到前一个线程的操作结果!(管程是一种通用的同步原语,synchronized就是管程的实现)
  • volatile变量规则:就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见
  • 线程启动规则:在主线程A执行过程中,启动子线程B,那么线程A在启动子线程B之前对共享变量的修改结果对线程B可见
  • 线程终结规则:在主线程A执行过程中,子线程B终止,那么线程B在终止之前对共享变量的修改结果在线程A中可见
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始(finalize:垃圾回收机器(Garbage Collection),也叫GC)
  • 传递性:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
各个层面需要实现保证并发安全

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

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

相关文章

Java实现籍贯级联选择器

在工作中要求写一个籍贯的级联选择器,记录一下自己写这个级联选择器的过程,因为自己才刚开始工作,有很多地方都没有考虑的很清楚,希望各位大佬能给出建议。 一、需求 A:正常的23个省,籍贯由“省区/县/市”组成&#xf…

javaScript:分支语句的理解与使用(附带案例)

目录 前言 补充 另一种说法 分支语句 1.if语句 a.单分支语句 注意 b.双分支语句 注意点 c.多分支语句(分支语句的联级语句) 补充 2.三元运算符 三元运算符 ? : 使用场景 3.switch语句 解释 释义&#xff1a…

2000-2022年全国地级市乡村振兴测算数据(30个指标)

1、时间:2000-2022年 2、来源:城市统计NJ、各地区统计NJ 3、范围:地级市 4、指标:乡村振兴指数、人均农业机械总动力(千瓦)、粮食综合生产能力(万吨)、农业劳动生产率&#xff08…

ToolLLM:能够使用 16000 种真实 API 的 LLM

随着对 LLM(大语言模型)的了解与使用越来越多,大家开始偏向于探索 LLM 的 agent 能力。尤其是让 LLM 学习理解 API,使用工具进行对用户的 instruction(指令)进行处理。然而,目前的开源大模型并不…

Oracle连接数据库提示 ORA-12638:身份证明检索失败

ORA-12638 是一个 Oracle 数据库的错误代码,它表示身份验证(认证)检索失败。这通常与数据库连接相关,可能由于以下几个原因之一引起: 错误的用户名或密码: 提供的数据库用户名或密码不正确,导致…

开源数据库Mysql_DBA运维实战 (DDL语句)

DDL DDL语句 数据库定义语言:数据库、表、视图、索引、存储过程. 例如:CREATE DROP ALTER DDL库 定义库{ 创建业务数据库:CREAATE DATABASE ___数据库名___ ; 数据库名要求{ a.区分大小写 b.唯一性 c.不能使用关键字如 create select d.不能单独使用…

单片机学到什么程度可以找到工作?

STM32是意法半导体公司推出一款32位的单片机,其具有超低的价格、超多的外设、丰富的型号、优异的实时性、极低的开发成本等优势,并且stm32相关资料都非常全面和细致,所以很适合小白的学习。对于开发方式的选择,选择一种适合自己的…

MySQL索引3——Explain关键字和索引使用规则(SQL提示、索引失效、最左前缀法则)

目录 Explain关键字 索引性能分析 Id ——select的查询序列号 Select_type——select查询的类型 Table——表名称 Type——select的连接类型 Possible_key ——显示可能应用在这张表的索引 Key——实际用到的索引 Key_len——实际索引使用到的字节数 Ref ——索引命…

day1 ARM架构概述

ARM处理器架构 1、指令集: 1.1、复杂指令集(CISC):包含处理复杂操作的特定指令,指令长度不固定,执行需要多个周期; 1.2、简单指令集(RISC):指令简单而有效,格式和长度通常是固定的,…

RISC-V公测平台发布 · 使用YCSB测试SG2042上的MySQL性能

实验介绍: YCSB(全称为Yahoo! Cloud Serving Benchmark),该性能测试工具由Java语言编写(在之前的MC文章中也提到过这个,如果没看过的读者可以去看看之前MC那一期),主要用于云端或者…

WebSocket整合spring 一文全部搞定

文章声明 本文简单整合了webSocket 组件,涉及到的源码分解,原理什么的以后再说,本文只适合入门小白体验,不涉及复杂业务逻辑。 文章目录 1 引入webSocket依赖包2 声明式整合WebSocket(这是一道硬菜)2.1 webSocket 配置类2.2 webs…

Java 数据库时间返回前端显示错误(差8个小时)

文章目录 JsonFormat 与 DateTimeFormat 使用0 可能错误截图1 在属性上加自定义Json返回注释 JsonSerialize2 新建实体类 CustomDateTimeSerializer3 前端传后端格式转换(ISO 日期格式)转(Data)4 一个注释解决双端转化问题 JsonFormat 与 Dat…

Java 本地缓存之王:Caffeine 保姆级教程

一、Caffeine介绍 1、缓存介绍 缓存(Cache)在代码世界中无处不在。从底层的CPU多级缓存,到客户端的页面缓存,处处都存在着缓存的身影。缓存从本质上来说,是一种空间换时间的手段,通过对数据进行一定的空间安排,使得下…

matplotlib 为图顶部和图右部的坐标轴添加标记label

Matplotlib 中,默认情况下,只有底部和左侧的坐标轴有标记 1 设置底部坐标轴标签 通过使用ax.xaxis.set_label_position() 调整标签的位置 import matplotlib.pyplot as plt# 创建一个图表 fig, ax plt.subplots()# 生成示例数据 x [1, 2, 3, 4, 5] …

激光焊接塑料多点测试全画面穿透率测试仪

工程塑料由于其具有高比强度、电绝缘性、耐磨性、耐腐蚀性等优点,已广泛应用于各个重要领域。另一方面,工程塑料还具有良好的焊接性,是制成复合材料的基体材料的优良选择,因此目前已成为国内外新型复合材料的研究热点。 工程塑料…

网络安全 Day27-运维安全项目-iptables防火墙

iptables防火墙 1. 防火墙概述2. 防火墙2.1 防火墙种类及使用说明2.2 必须熟悉的名词2.3 iptables 执行过程※※※※※2.4 表与链※※※※※2.4.1 简介2.4.2 每个表说明2.4.2.1 filter表 :star::star::star::star::star:2.4.2.2 nat表 2.5 环境准备及命令2.6 案例01&#xff1a…

c基础扫雷

和三子棋一样,主函数先设计游戏菜单界面,这里就不做展示了。 初始化棋盘 初级扫雷大小为9*9的棋盘,但排雷是周围一圈进行排雷(8格),而边界可能会越界。数组扩大了一圈,行和列都加了2,所以我们用一个11*11的数组来初始化…

【论文阅读】基于深度学习的时序预测——Informer

系列文章链接 论文一:2020 Informer:长序列数据预测 论文二:2021 Autoformer:长序列数据预测 文章地址:https://arxiv.org/abs/2012.07436 github地址:https://github.com/zhouhaoyi/Informer2020 参考解读…

MySql之主从复制延时

MySql之主从复制延时 一、MySQL主从复制模型 一切都要从MySQL的主从复制模型开始说起,下图是最经典的MySQL主从复制模型架构图: 主从架构依赖于MySQL Binlog功能,Master节点上产生Binlog并将Binlog写入到Binlog文件中。 Slave节点上启动两…

java+springboot+mysql小区宠物管理系统

项目介绍: 使用javaspringbootmysql开发的小区宠物管理系统,系统包含超级管理员,系统管理员、用户角色,功能如下: 超级管理员:管理员管理;用户管理;宠物分类;宠物管理&…