JavaEE之线程(5)——Java内存模型、内存可见性、volatile关键字

前言

volatile可以理解成轻量级的 synchronized, 它在多CPU开发中保证了共享变量的“可见性”,可见性我们可以理解成是:当一个线程修改一个共享变量时,另一个线程可以读到这个修改的值。由于它不会引起线程的上下文切换和调度,所以如果对volatile使用恰当的话,它比synchronized的使用成本更低。


一、内存可见性和内存模型

1.1内存可见性

基本概念:
 可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到

1.2 内存模型(重点)

 Java 内存模型 (JMM):Java虚拟机规范中定义了Java内存模型目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果。
线程,主内存,工作内存的交互关系如图所示:
在这里插入图片描述
简单来说:

  1. 所有变量储存在主内存。
  2. 每条线程拥有自己的工作内存,其中保存了主内存中线程使用到的变量的副本。
  3. 线程不能直接读写主内存中的变量,所有操作均在工作内存中完成。

由上述内容可知,每个线程有自己的工作内存,这些工作内存中的内容相当于同一个共享变量的 “副本”。
(1)此时修改线程1 的工作内存中的值,线程2 的工作内存不一定会及时变化,如下图所示:
在这里插入图片描述
(2)一旦线程1 修改了 a 的值,此时主内存不一定能及时同步。对应的线程2 的工作内存的 a 的值也不一定能及时同步。此时,这个时候代码中就容易出现问题。
在这里插入图片描述

二、volatile关键字

 在上面,我们介绍了内存可见性问题,我们在写入 volatile 修饰的变量的时候

代码在写入 volatile 修饰的变量的时候;
将改变后的副本的值从工作内存刷新到主内存

代码在读取 volatile 修饰的变量的时候

从主内存中读取volatile变量的最新值到线程的工作内存中
从工作内存中读取volatile变量的副本

下面我们从一个实例来详细介绍volatile关键字

static class Counter {
	public int flag = 0;
}
public static void main(String[] args) {
	Counter counter = new Counter();
	
	Thread t1 = new Thread(() -> {
		while (counter.flag == 0) {
		// do nothing
	}
	System.out.println("循环结束!");
	});
	
	Thread t2 = new Thread(() -> {
		Scanner scanner = new Scanner(System.in);
		System.out.println("输入一个整数数:");
		counter.flag = scanner.nextInt();
	});
	t1.start();
	t2.start();
}

----------------------------------------------------
// 执行效果
// 当用户输入非0值时, t1 线程循环不会结束. (这显然是一个 bug)

此时,t1 读的是自己工作内存中的内容,当 t2 对 flag 变量进行修改,此时 t1 感知不到 flag 的变化。
但是如果给变量加上volatile关键字,代码就不会出错:

static class Counter {
	public volatile int flag = 0;
}
-----------------------------------
// 执行效果
// 当用户输入非0值时, t1 线程循环能够立即结束.

三、volatile 不保证原子性

volatile 和 synchronized 有着本质的区别.,synchronized 能够保证原子性, volatile 保证的是内存可见性。
比如,我们对上面的代码进行调整:去掉 flag 的 volatile,给 t1 的循环内部加上 synchronized,并借助 counter 对象加锁。

static class Counter {
	public int flag = 0;
}

public static void main(String[] args) {
	Counter counter = new Counter();
	Thread t1 = new Thread(() -> {
		while (true) {
			synchronized (counter) {
				if (counter.flag != 0) {
					break;
				}
			}
			// do nothing
		}
		System.out.println("循环结束!");
	});
	Thread t2 = new Thread(() -> {
		Scanner scanner = new Scanner(System.in);
		System.out.println("输入一个整数:");
		counter.flag = scanner.nextInt();
	});
	t1.start();
	t2.start();
}

总结

 以上就是今天要讲的内容,在并发三特征的可见性中,volatile通过新值立即同步到主内存和每次使用前从主内存刷新机制保证了可见性。通过禁止指令重排序保证了有序性。无法保证原子性;
 synchronized关键字通过lock和unlock操作保证了原子性,通过对一个变量unlock前,把变量同步回主内存中保证了可见性,通过一个变量在同一时刻只允许一条线程对其进行lock操作保证了有序性

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

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

相关文章

【JavaWeb】Day77.Spring——SpringBoot原理(一)

SpringBoot原理 Spring是目前世界上最流行的Java框架,它可以帮助我们更加快速、更加容易的来构建Java项目。而在Spring家族当中提供了很多优秀的框架,而所有的框架都是基于一个基础框架的SpringFramework(也就是Spring框架)。而如果我们直接基于Spring框…

【Shell】Shell编程之函数

目录 1.Shell函数定义 2.Shell函数的作用 3.函数返回值 4.函数传参 5.函数变量的作用范围 案例 1.Shell函数定义 格式1 function 函数名 { 命令序列 } 格式2 函数名() { 命令序列 } 2.Shell函数的作用 使用函数可以避免代码重复 使用函数可以将大的工程分割为若…

springcloud简单了解及上手

springcloud微服务框架简单上手 文章目录 springcloud微服务框架简单上手一、SpringCloud简单介绍1.1 单体架构1.2 分布式架构1.3 微服务 二、SpringCloud与SpringBoot的版本对应关系2022.x 分支2021.x 分支2.2.x 分支 三、Nacos注册中心3.1 认识和安装Nacos3.2 配置Nacos3.3 n…

【ARM Cortex-M 系列 2.3 -- Cortex-M7 Debug event 详细介绍】

请阅读【嵌入式开发学习必备专栏】 文章目录 Cortex-M7 Debug eventDebug events Cortex-M7 Debug event 在ARM Cortex-M7架构中,调试事件(Debug Event)是由于调试原因而触发的事件。一个调试事件会导致以下几种情况之一发生: 进…

部署管理征信链码

一 . 链码准备 需要删除上面后面标记的文件,之后拖入 二. 打包链码 注意需要先启动链 打包测试链码 export FABRIC_CFG_PATH${PWD}/config peer lifecycle chaincode package ./chaincode/chaincode_basic.tar.gz --path ./chaincode/credit_chaincode --lang n…

vue3.0(五) reactive全家桶

文章目录 1 reactive1.1 reactive的应用1.2 reactive的特点1.3 reactive的注意1.4 reactive的局限性 2 toRefs3 isReactive4 shallowReactive5 readonly5.1 readonly 详细信息5.2 readonly函数创建一个只读的响应式对象5.3 如何修改嵌套在只读响应式对象中的对象? 6 isReadonl…

考研数学|李林《880》做不动,怎么办!?看这一篇!

在考研数学的备考过程中,遇到难题是很常见的情况,尤其是当你尝试解决李林880习题集中的问题时。他以其难度和深度著称,旨在帮助考生深入理解数学分析的复杂概念。 如果你在解题过程中感到困难,这并不是你个人的问题,而…

《无畏契约》游戏画面出现“撕裂感“,你清楚背后的原理吗?

🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏:🍕 Collection与数据结构 (91平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 🧀Java …

安全风险 - 如何解决 setAccessible(true) 带来的安全风险?

可能每款成熟的金融app上架前都会经过层层安全检测才能执行上架,所以我隔三差五就能看到安全检测报告中提到的问题,根据问题的不同级别,处理的优先级也有所不同,此次讲的主要是一个 “轻度问题” ,个人认为属于那种可改…

[译文] 恶意代码分析:2.LNK文件伪装成证书传播RokRAT恶意软件(含无文件攻击)

这是作者新开的一个专栏,主要翻译国外知名安全厂商的技术报告和安全技术,了解它们的前沿技术,学习它们威胁溯源和恶意代码分析的方法,希望对您有所帮助。当然,由于作者英语有限,会借助LLM进行校验和润色&am…

idea控制台日志控制

1.清除控制台log日志 测试的时候,控制台打印的日志比较多,速度有点慢而且不利于查看运行结果,所以接下来我们把这个日志处理下: 取消初始化spring日志打印,resources目录下添加logback.xml,名称固定,内容如…

Transformer+Classification学习笔记

论文名称:An Image is Worth 16x16 Words:Transformers for Image Recognition at Scale [2112.11010] MPViT: Multi-Path Vision Transformer for Dense Prediction (arxiv.org) 参考博客与视频: Vision Transformer 超详细解读 (原理分析代码解读) …

[动画详解]LeetCode151.翻转字符串里的单词

💖💖💖欢迎来到我的博客,我是anmory💖💖💖 又和大家见面了 欢迎来到动画详解LeetCode算法系列 用通俗易懂的动画让算法题不再神秘 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低成…

589.N叉树的前序遍历

刷算法题: 第一遍:1.看5分钟,没思路看题解 2.通过题解改进自己的解法,并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步,下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…

解决el-upload组件上传文件403 Forbidden的问题

话不多说,上错误。网络显示: 控制台显示: 并且后端也没接收到任何的请求。 只需要把前端中的组件: action的路径修改为: 也就是不写前面的localhost,而是拼接上发送请求拼接的‘api’即可 可以看到&#x…

架构每日一学 6:作为架构师,你必须学会寻找商业模式

本文首发于公众平台:腐烂的橘子 在前面的文章中,我们已经讲了架构师的两条生存法则,第一条是有且仅有一个目标,感兴趣的可以看一下原文: 架构每日一学 2:架构师六个生存法则之一:架构必须有且仅…

【LLM第五篇】名词解释:prompt

1.是什么 提示工程(Prompt Engineering)是一门较新的学科,关注提示词开发和优化,帮助用户将大语言模型(Large Language Model, LLM)用于各场景和研究领域。 掌握了提示工程相关技能将有助于用户更好地了解…

教育型内容的制胜秘诀:Kompas.ai如何结合知识与营销

在数字化营销的浪潮中,教育型内容已经成为品牌建立权威性和提供价值的重要手段。通过分享专业知识和见解,品牌不仅能够吸引目标受众,还能够在潜在客户心中建立起专业和可信赖的形象。本文将深入分析教育型内容的重要性,详细介绍Ko…

sklearn之k近邻算法——以鸢尾花分类为例

文章目录 k近邻算法算法原理k值的选取特征数据的归一化距离的度量分类原则的制定鸢尾花分类 k近邻算法 k近邻算法是经典的监督学习算法,我们这里主要介绍k近邻算法的基本内容和如何应用 算法原理 k近邻算法的基本原理其实很简单 首先k近邻算法是一个分类算法&am…

x264 帧类型代价计算原理:slicetype_slice_cost 函数分析

x264 x264 是一个开源的视频编码库,它实现了H.264/AVC标准。H.264是一种广泛使用的压缩标准,用于视频流、视频下载、蓝光光盘以及许多其他形式的数字视频分发。x264 以其高压缩效率和良好的视频质量而著称,是许多视频编辑软件和视频播放器的默认编解码器。 以下是关于 x26…