深入学习并发编程中的synchronized

news2024/11/14 10:29:20

第一章:并发编程中的三个问题

可见性

可见性概念
可见性( Visibility ):是指一个线程对共享变量进行修改,另一个先立即得到修改后的最新值。
可见性演示
案例演示:一个线程根据 boolean 类型的标记 flag while 循环,另一个线程改变这个 flag 变量的值,另 一个线程并不会停止循环。
package com . itheima . concurrent_problem ;
/**
案例演示 :
一个线程对共享变量的修改 , 另一个线程不能立即得到最新值
*/
public class Test01Visibility {
// 多个线程都会访问的数据,我们称为线程的共享数据
private static boolean run = true ;
public static void main ( String [] args ) throws InterruptedException {
Thread t1 = new Thread (() -> {
while ( run ) {
}
});
t1 . start ();
Thread . sleep ( 1000 );
Thread t2 = new Thread (() -> {
run = false ;
System . out . println ( " 时间到,线程 2 设置为 false" );
});
t2 . start ();
}
}

小结

并发编程时,会出现可见性问题,当一个线程对共享变量进行了修改,另外的线程并没有立即看到修改后的最新值。

原子性

原子性概念
原子性( Atomicity ):在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中
断,要么所有的操作都不执行。
原子性演示
案例演示 :5 个线程各执行 1000 i++;
使用 javap 反汇编 class 文件,得到下面的字节码指令:
package com . itheima . demo01_concurrent_problem ;
import java . util . ArrayList ;
/**
案例演示 :5 个线程各执行 1000 i++;
*/
public class Test02Atomicity {
private static int number = 0 ;
public static void main ( String [] args ) throws InterruptedException {
Runnable increment = () -> {
for ( int i = 0 ; i < 1000 ; i ++ ) {
number ++ ;
}
};
ArrayList < Thread > ts = new ArrayList <> ();
for ( int i = 0 ; i < 5 ; i ++ ) {
Thread t = new Thread ( increment );
t . start ();
ts . add ( t );
}
for ( Thread t : ts ) {
t . join ();
}
System . out . println ( "number = " + number );
}
}
其中,对于 number++ 而言( number 为静态变量),实际会产生如下的 JVM 字节码指令:
由此可见 number++ 是由多条语句组成,以上多条指令在一个线程的情况下是不会出问题的,但是在多线程情况下就可能会出现问题。比如一个线程在执行13: iadd 时,另一个线程又执 9:getstatic 。会导致两次number++ ,实际上只加了 1

小结

并发编程时,会出现原子性问题,当一个线程对共享变量操作到一半时,另外的线程也有可能来操作共享变量,干扰了前一个线程的操作。

有序性

有序性概念
有序性( Ordering ):是指程序中代码的执行顺序, Java 在编译时和运行时会对代码进行优化,会导致程序最终的执行顺序不一定就是我们编写代码时的顺序。
有序性演示
jcstress java 并发压测工具。 https://wiki.openjdk.java.net/display/CodeTools/jcstress
修改 pom 文件,添加依赖:
9: getstatic #12 // Field number:I
12: iconst_1
13: iadd
14: putstatic #12 // Field number:I
public static void main ( String [] args ) {
int a = 10 ;
int b = 20 ;
} <dependency>
<groupId> org.openjdk.jcstress </groupId>
<artifactId> jcstress-core </artifactId>
<version> ${jcstress.version} </version>
</dependency>
代码
Test03Orderliness.java
package com . itheima . concurrent_problem ;
import org . openjdk . jcstress . annotations . * ;
import org . openjdk . jcstress . infra . results . I_Result ;
@JCStressTest
@Outcome ( id = { "1" "4" } expect = Expect . ACCEPTABLE desc = "ok" )
@Outcome ( id = "0" expect = Expect . ACCEPTABLE_INTERESTING desc = "danger" )
@State
public class Test03Orderliness {
int num = 0 ;
boolean ready = false ;
// 线程一执行的代码
@Actor
public void actor1 ( I_Result r ) {
if ( ready ) {
r . r1 = num + num ;
} else {
r . r1 = 1 ;
}
}
// 线程 2 执行的代码
@Actor
public void actor2 ( I_Result r ) {
num = 2 ;
ready = true ;
}
}
Result 是一个对象,有一个属性 r1 用来保存结果,在多线程情况下可能出现几种结果?
情况 1 :线 程1 先执行 actor1 ,这时 ready = false ,所以进入 else 分支结果为 1
情况 2 :线程 2 执行到 actor2 ,执行了 num = 2; ready = true ,线程 1 执行,这回进入 if 分支,结果为 4
情况 3 :线程 2 先执行 actor2 ,只执行 num = 2 ;但没来得及执行 ready = true ,线程 1 执行,还是进入else分支,结果为 1
还有一种结果 0
运行测试:
mvn clean install
java - jar target / jcstress . jar

小结

程序代码在执行过程中的先后顺序,由于 Java 在编译期以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序。

第二章:Java内存模型(JMM)

在介绍 Java 内存模型之前,先来看一下到底什么是计算机内存模型。

计算机结构简介

冯诺依曼,提出计算机由五大组成部分,输入设备,输出设备存储器,控制器,运算器。
CPU
中央处理器,是计算机的控制和运算的核心,我们的程序最终都会变成指令让 CPU 去执行,处理程序中的数据。
内存
我们的程序都是在内存中运行的,内存会保存程序运行时的数据,供 CPU 处理。
缓存
CPU 的运算速度和内存的访问速度相差比较大。这就导致 CPU 每次操作内存都要耗费很多等待时间。内 存的读写速度成为了计算机运行的瓶颈。于是就有了在CPU 和主内存之间增加缓存的设计。最靠近 CPU 的缓存称为L1 ,然后依次是 L2 L3 和主内存, CPU 缓存模型如图下图所示。 CPU Cache 分成了三个级别 : L1 L2 L3 。级别越小越接近 CPU ,速度也更快,同时也代表着容量越小。

小结

计算机的主要组成 CPU ,内存,输入设备,输出设备。

Java内存模型

Java 内存模型的概念
Java Memory Molde (Java 内存模型 /JMM) ,千万不要和 Java 内存结构混淆
关于 “Java 内存模型 的权威解释,请参考 https://download.oracle.com/otnpub/jcp/memory_model-
1.0-pfd-spec-oth-JSpec/memory_model-1_0-pfd-spec.pdf
Java 内存模型,是 Java 虚拟机规范中所定义的一种内存模型, Java 内存模型是标准化的,屏蔽掉了底层 不同计算机的区别。
Java 内存模型是一套规范,描述了 Java 程序中各种变量 ( 线程共享变量 ) 的访问规则,以及在 JVM 中将变量存储到内存和从内存中读取变量这样的底层细节,具体如下。
主内存
主内存是所有线程都共享的,都能访问的。所有的共享变量都存储于主内存。
工作内存
每一个线程有自己的工作内存,工作内存只存储该线程对共享变量的副本。线程对变量的所有的操
( 读,取 ) 都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接
访问对方工作内存中的变量。
Java 内存模型的作用
Java 内存模型是一套在多线程读写共享数据时,对共享数据的可见性、有序性、和原子性的规则和保障。
CPU 缓存,内存与 Java 内存模型的关系
通过对前面的 CPU 硬件内存架构、 Java 内存模型以及 Java 多线程的实现原理的了解,我们应该已经意识 到,多线程的执行最终都会映射到硬件处理器上进行执行。
Java 内存模型和硬件内存架构并不完全一致。对于硬件内存来说只有寄存器、缓存内存、主内存的概 念,并没有工作内存和主内存之分,也就是说Java 内存模型对内存的划分对硬件内存并没有任何影响,因为JMM 只是一种抽象的概念,是一组规则,不管是工作内存的数据还是主内存的数据,对于计算机硬 件来说都会存储在计算机主内存中,当然也有可能存储到CPU 缓存或者寄存器中,因此总体上来说,Java内存模型和计算机硬件内存架构是一个相互交叉的关系,是一种抽象概念划分与真实物理硬件的交叉。
JMM 内存模型与 CPU 硬件内存架构的关系:

小结

Java 内存模型是一套规范,描述了 Java 程序中各种变量 ( 线程共享变量 ) 的访问规则,以及在 JVM 中将变量 存储到内存和从内存中读取变量这样的底层细节,Java 内存模型是对共享数据的可见性、有序性、和原子性的规则和保障。

主内存与工作内存之间的交互目标

了解主内存与工作内存之间的数据交互过程
Java 内存模型中定义了以下 8 种操作来完成,主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存之类的实现细节,虚拟机实现时必须保证下面 提及的每一种操作都是原子的、不可再分的。
对应如下的流程图:
注意 :
1. 如果对一个变量执行 lock 操作,将会清空工作内存中此变量的值
2. 对一个变量执行 unlock 操作之前,必须先把此变量同步到主内存中
小结
lock -> read -> load -> use -> assign -> store -> write -> unlock
主内存与工作内存之间的数据交互过程

第三章:synchronized保证三大特性

synchronized 能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
synchronized ( 锁对象 ) {
// 受保护资源

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

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

相关文章

【C++BFS算法 二分查找】1631. 最小体力消耗路径

本文涉及知识 CBFS算法 C二分查找 LeetCode1631. 最小体力消耗路径 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights &#xff0c;其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) &#xff0c;且你希望去最右下…

【无标题】智能加速计算卡设计原理图:628-基于VU3P的双路100G光纤加速计算卡 XCVU3P板卡

基于VU3P的双路100G光纤加速计算卡 一、板卡概述 基于Xilinx UltraScale16 nm VU3P芯片方案基础上研发的一款双口100 G FPGA光纤以太网PCI-Express v3.0 x16智能加速计算卡&#xff0c;北京太速科技该智能卡拥有高吞吐量、低延时的网络处理能力以及辅助CPU进行网络功…

【深入探秘Hadoop生态系统】全面解析各组件及其实际应用

深入探秘Hadoop生态系统&#xff1a;全面解析各组件及其实际应用 引言 在大数据时代&#xff0c;如何高效处理和存储海量数据成为企业面临的重大挑战。根据Gartner的统计&#xff0c;到2025年&#xff0c;全球数据量将达到175泽字节&#xff08;ZB&#xff09;&#xff0c;传…

07 输入捕获和编码器接口

前言 前面介绍了定时器和输出比较&#xff0c;这一节主要介绍一下输入捕获测量输入频率和PWM占空比&#xff0c;然后介绍一下编码器接口。 一、输入捕获 1.什么是输入捕获 当输入的引脚有指定电平跳变时&#xff0c;会将计数器CNT中的值保存在CCR中&#xff0c;这个就称为输…

JDK 1.8从下载、安装、配置、以及检查是否安装成功,最详细教学教程

参考&#xff1a; JDK1.8下载、安装和环境配置教程(2024年6月5日)-CSDN博客 以下所有步骤&#xff0c;jjycheng作者亲测,所以截图是我自己截取的&#xff0c;和原文略有不同。这也是为什么我可耻的选择“原创”的原因。。。哈哈。。。 一、下载安装包 链接&#xff1a;https:/…

pycharm安装与配置Pyqt5

pycharm安装与配置Pyqt5 1、创建项目、虚拟环境 打开pycharm&#xff0c;File->New Project 2、安装pyqt5库 在pycharm下方Terminal终端窗口输入&#xff1a; pip install PyQt5 -i https://pypi.douban.com/simple pip install PyQt5-tools -i https://pypi.douban.c…

【C++】简约与清晰的编程艺术

C编程的艺术&#xff1a;简约与清晰的实践之道 一、基础之美&#xff1a;基本类型与数据结构的力量二、函数与库类的艺术三、简约与清晰的实践之道 在C这一既古老又充满活力的编程语言世界里&#xff0c;程序员们常常面临着一个重要的选择&#xff1a;是追求代码的极致抽象与封…

【Elasticsearch】Elasticsearch的分片和副本机制

文章目录 &#x1f4d1;前言一、分片&#xff08;Shard&#xff09;1.1 分片的定义1.2 分片的重要性1.3 分片的类型1.4 分片的分配 二、副本&#xff08;Replica&#xff09;2.1 副本的定义2.2 副本的重要性2.3 副本的分配 三、分片和副本的机制3.1 分片的创建和分配3.2 数据写…

发送邮件API接口的安全性保障措施有哪些?

发送邮件API接口的性能如何评估&#xff1f;API接口的使用方法&#xff1f; 发送邮件API接口已经成为了许多应用和服务的核心功能之一。确保发送邮件API接口的安全性对于保护用户隐私和数据完整性至关重要。AokSend将详细探讨发送邮件API接口的安全性保障措施。 发送邮件API接…

查找算法:线性查找,golang实现

目录 前言 线性查找 代码示例 1. 算法包 2. 线性查找代码 3. 模拟程序 4. 运行程序 循环次数 假如目标值正好在数组中的第一位 假如目标值正好在数组中的第五位 假如目标值正好在数组中的最后一位 假如目标值不在数组中 线性查找的思想 1. 顺序遍历 2. 比较 3.…

使用GPT-4插件增强LLM的功能

文章目录 GPT-4插件概述插件清单OpenAPI规范描述GPT-4插件 尽管包括GPT-4在内的LLM在各种任务上都现出色,但它们仍然存在面有的局限性。比如,这些模型只能从训练数据中学习,这些数据往往过时或不造用于特定的应用。此外,它们的能力仅限于文本生成。我们还发现,LLM不适应于…

人工智能深度学习系列—深度学习中的边界框回归新贵:GHM(Generalized Histogram Loss)全解析

文章目录 1. 背景介绍2. Loss计算公式3. 使用场景4. 代码样例5. 总结 1. 背景介绍 目标检测作为计算机视觉领域的核心技术之一&#xff0c;其精确度的提升一直是研究者们追求的目标。边界框回归作为目标检测中的关键步骤&#xff0c;其性能直接影响到检测的准确性。本文将详细…

“火炬科企对接”先进计算产业推进会 | 麒麟信安受邀参加,并签署开源生态合作协议

7月30日&#xff0c;“火炬科企对接”先进计算产业推进会在长沙隆重召开。大会由工业和信息化部火炬高技术产业开发中心、湖南省科学技术厅、湖南省工业和信息化厅、湖南湘江新区管理委员会、中国邮政储蓄银行联合举办。麒麟信安与来自国内先进计算领域的专家学者&#xff0c;2…

25考研数据结构复习·7.3树形查找

目录 二叉排序树 平衡二叉树 平衡二叉树的删除 红黑树 红黑树的插入 红黑树的删除 二叉排序树 二叉排序树的定义 &#x1f469;‍&#x1f4bb; 左子树结点值 < 根节点值 < 右子树结点值默认不允许两个结点的关键字相同查找操作 从根节点开始&#xff0c;目标值更…

用 VS Code 开发 uni-app 项目

文章目录 1.为什么选择 VS Code &#xff1f;2.安装相关插件2.1 安装uni-app插件2.2 安装ts类型检验 3.在微信小程序中运行 1.为什么选择 VS Code &#xff1f; ⚫ HbuilderX 对 TS 类型支持暂不完善 ⚫ VS Code 对 TS 类型支持友好&#xff0c;同时VS Code 也是我们熟悉的编辑…

移远通信LTE-A模组EM060K-GL成为ChromeOS准入供应商

近日&#xff0c;全球领先的物联网整体解决方案移远通信宣布&#xff0c;其先进的LTE-A模组EM060K-GL已进入谷歌Chrome OS准入供应商名录&#xff0c;后续将作为候选模组用于搭载Chrome OS系统的笔记本电脑中&#xff0c;为其提供“始终在线”的网络连接体验。这一重要里程碑彰…

禾川Q1系列PLC通过ModbusRtu控制E600变频器

一、新建CodeSys工程项目 新建工程可以选择【File】→【New Project】,也可以直接选择【New Project】两种方式 用户可以选择需要的项目类型,命名项目工程以及存储路径,完成后选择【OK】 选择连接设备【HCQ1-1300-D】(在此之前PC已经安装Q1安装包),选择编程语言,教程示例…

Node.js(4)——模块化

什么是模块化&#xff1f; ComminJS模块是为Node.js打包JavaScript代码的原始方式。Node.js还支持浏览器和其他JavaScript运行时使用的ECMAScript标准。在Node.js中&#xff0c;每个文件都被视为一个单独的模块。 CommonJS标准 使用&#xff1a; 导出&#xff1a;moudule.exp…

一六零、云服务器开发机配置zsh

切换shell 在Linux中默认使用/bin/bash&#xff0c;在用户创建时&#xff0c;会自动给用户创建用户默认的shell。默认的shell就是/bin/bash。要修改shell将其设置为/bin/ksh&#xff0c;有两种方法方法 # 方法一: chsh -s /bin/ksh chsh -s /bin/zsh # 方法二: usermod -s /b…

基于CIFAR10的图片识别

前言 这个算是重拾一个古早项目了&#xff0c;当时搭建神经网络对CIFAR10数据集进行训练以后&#xff0c;对训练好的网络进行了验证&#xff0c;可以参考这笔者的两篇博客&#xff1a; pytorch 模型训练&#xff08;以CIFAR10数据集为例&#xff09;_pytorch cifar10-CSDN博客…