并发编程 - 线程同步(一)
经过前面对线程的尝试使用,我们对线程的了解又进一步加深了。今天我们继续来深入学习线程的新知识 —— 线程同步。
01、什么是线程同步
线程同步是指在多线程环境下,确保多个线程在同时使用共享资源时不会发生冲突或数据不一致问题的技术,保证线程间的正确协作。它的目的是使得多个线程在执行过程中能够按照某种顺序、安全地使用共享资源。
02、为何需要线程同步
1、避免竞争条件
不知道大家还记得在《并发编程 - 初识线程》中出现的关键字volatile和特性ThreadStatic吗?它们都是为了解决多线程共享资源问题。
在多线程中当多个线程需要同时使用共享资源时,很容易产生互相竞争资源使用权的情况,这一问题也叫竞争条件。此时就可以通过线程同步技术实现多个线程按顺序使用共享资源,从而避免竞争条件。
2、保证共享资源安全
我们举个简单的例子,假如我的银行账户里有1000元,此时我正在用电子银行在线上操作准备向我老婆的账户里转账100元,而恰巧此时我老婆拿着我的银行卡准备取款500。
假如银行系统还是一个只有多线程,没有线程同步功能的老系统,在这一前置条件下。假如恰巧我们俩在同一瞬间点了确认操作,相信此时系统会发生什么?
有可能会是系统同时收到我们俩的请求,此时我的操作线程A,首先读取我账户余额1000,然后执行转账操作把余额减100得到900,再更新至余额中。而我老婆的操作线程B因为是和我同时的,所以在读取我账户余额的时候得到的也是1000,而不是900,此时线程B执行取款500操作把余额减500得到500,再更新至余额中。
可以发现我们俩最后更新余额,无论谁更新成功最后结果都是不正确的。这个例子就导致银行账户余额最终不正确,也就是我们说的共享资源不安全。如果使用线程同步,使得线程A、B可以按顺序执行,无论谁先执行最终结果都会是正确的。
下面我们再来结合代码举一个经典问题 —— torn read。
先解释一下什么叫torn read,可以翻译成一次读取被撕成两半。或者说在机器级别上,要分两个MOV指令才能读完。
具体来说就是一个long类型变量_var,当一个线程把_var赋值为0x0123456789ABCDEF,而此时另一个线程来读取_var,结果读取的值是0x0123456700000000或0x0000000089ABCDEF。这同样是因为多线程导致的共享资源不安全问题。
下面看看模拟代码实现效果:
public class ThreadSync
{//共享的int64变量public static long _var; public static void Run(){//启动写入线程var writerThread = new Thread(WriteToSharedValue);//启动读取线程var readerThread = new Thread(ReadFromSharedValue);//启动线程writerThread.Start();readerThread.Start();//等待线程执行完成writerThread.Join();readerThread.Join();}//写入线程static void WriteToSharedValue(){//模拟分两步写入long high = 0x01234567;long low = 0x89ABCDEF;unsafe{//将 _var 分成高低两部分写入//写高 32 位_var = high << 32;// 确保读取线程能在这里读取中间值Thread.Sleep(0); //写低 32 位_var |= low;}Console.WriteLine($"写: 写入值 0x{_var:X16}");}//读取线程static void ReadFromSharedValue(){// 读取共享变量的值Console.WriteLine($"读: 读取值 0x{_var:X16}");}
}
我们看下执行效果:
当然上面的例子并不是每次都会出现的,可能需要多运行几次,另外关于写入线程为什么不是直接赋值而是把值拆成高低位分两次写入?
这是因为我的电脑是64位系统,在大多数现代的 x64 系统架构(例如 Intel 和 AMD 处理器)上,64 位的原子性操作通常是被保证的。即使对于像 long(64 位)这种数据类型,处理器通常会在硬件层面确保它的读写操作是原子性的,因此,不太容易发生撕裂的读(torn read)。
所以这里的代码把一次赋值行为认为拆解成两步,同时Thread.Sleep(0)也为了让当前线程主动让出 CPU 时间片,使读线程有机会读取,使其更贴近在x32环境下运行的情况。如果有条件可以用直接赋值再x32环境下看看效果。
03、如何实现线程同步
1、避免资源共享
当然严格意义上说可能这一条不算是线程同步,只能说解决了多线程碰到的问题,达到线程同步的效果。
如果没有共享资源,那么自然就无须进行线程同步。大多数时候可以通过重新设计程序来除移共享状态,从而去掉复杂的同步构造。尽可能避免在多个线程间使用单一对象。
除了通过重新设计来移除共享状态,还可以通过语言特性设计使其达到无共享状态。比如值类型在传递过程中总是被复制,每个线程都会有自己的数据副本,比如看下面这个方法:
public static int Max(int val1, int val2)
{ return val1 > val2 ? val1 : val2;
}
即使这个方法没有使用任何线程同步方法,这个方法也是线程安全的。因为值类型特性原因,所以传给Max的两个int值会复制到方法内部,形成自己的数据副本。此时无论有多少个线程调用Max方法,每个线程处理的都是它自己的数据,线程之间并不会互相干扰。
2、用户模式同步机制
用户模式同步机制指在用户空间内完成线程的阻塞和唤醒操作,由程序自己管理同步对象的一种同步方式,因为不涉及与操作系统内核交换,因此开销较低,更轻量级。
实现方式有SpinLock、SpinWait、Monitor(lock)等。
3、内核模式同步机制
内核模式同步机制是指在操作系统内核空间就完成线程的挂起与恢复,由操作系统管理同步对象的一种同步方式,因为每次线程同步操作都需要操作系统参与,因此必然回涉及内核态的上下文切换,同时还是涉及到操作系统内部的数据结构和资源管理,因此内核模式同步机制往往会导致较高的开销。
实现方式有Semaphore、Mutex、AutoResetEvent等。
4、混合模式同步机制
混合模式同步机制在某些情况下会根据线程竞争的情况在用户模式和内核模式之间切换。通常,当资源访问冲突较小或线程阻塞较少时,采用用户模式同步;当资源争用较多或有较大的线程等待时,自动切换到内核模式同步。
实现方式有SemaphoreSlim、ManualResetEventSlim、CountDownEvent、Barrier、ReaderWriterLockSlim等。
注:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner
相关文章:

并发编程 - 线程同步(一)
经过前面对线程的尝试使用,我们对线程的了解又进一步加深了。今天我们继续来深入学习线程的新知识 —— 线程同步。 01、什么是线程同步 线程同步是指在多线程环境下,确保多个线程在同时使用共享资源时不会发生冲突或数据不一致问题的技术,保…...

PyTorch入门 - 为什么选择PyTorch?
PyTorch入门 - 为什么选择PyTorch? Entry to PyTorch - Why PyTorch? by JacksonML $ pip install pytorch安装完毕后,可以使用以下命令,导入第三方库。 $ import pytorch...

leetcode刷题记录(八十六)——84. 柱状图中最大的矩形
(一)问题描述 84. 柱状图中最大的矩形 - 力扣(LeetCode)84. 柱状图中最大的矩形 - 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾…...

Android GLSurfaceView 覆盖其它控件问题 (RK平台)
平台 涉及主控: RK3566 Android: 11/13 问题 在使用GLSurfaceView播放视频的过程中, 增加了一个播放控制面板, 覆盖在视频上方. 默认隐藏setVisibility(View.INVISIBLE);点击屏幕再显示出来. 然而, 在RK3566上这个简单的功能却无法正常工作. 通过缩小视频窗口可以看到, 实际…...

开源鸿蒙开发者社区记录
lava鸿蒙社区可提问 Laval社区 开源鸿蒙项目 OpenHarmony 开源鸿蒙开发者论坛 OpenHarmony 开源鸿蒙开发者论坛...

【Linux网络编程】传输层协议
目录 一,传输层的介绍 二,UDP协议 2-1,UDP的特点 2-2,UDP协议端格式 三,TCP协议 3-1,TCP报文格式 3-2,TCP三次握手 3-3,TCP四次挥手 3-4,滑动窗口 3-5…...

10个非常基础的 Javascript 问题
Javascript是一种用于Web开发的编程语言。JavaScript在网络的客户端上运行。 根据MDN,JavaScript(通常缩写为JS)是一种轻量级的,解释性的,面向对象的语言,具有一流的功能,并且最著名的是Web页面…...

Mysql索引(学习自用)
目录 一、索引概述 优缺点 二、索引结构 1、索引数据结构 2、索引支持结构 3、B树 4、B树 5、hash索引 6、为啥采用B树索引 三、索引分类 四、索引语法 五、索引性能分析 5.1查看执行频率 5.2慢查询日志 5.3profiling 5.4explain 六、索引使用规则 6.1验证索…...
eniops库中reduce函数使用方法
reduce 是 eniops 中的一个常用函数,用于对张量进行降维操作。它允许你通过指定维度名称和操作类型(如求和、均值等)来简化张量的形状。 import eniops import torch# 创建一个示例张量 x torch.randn(2, 3, 4)# 使用 reduce 进行降维操作 …...

阴沟翻船题——Longest Substring Without Repeating Characters
一、事件概述 今天接到一个面试,让线上做题。面试官出了个leetcode的题。题目如图所示: 我没有刷过leetcode,上学时候我们做的hdu-acm和codeforces。咋一接到题目,看到是个字符串题,并且找最长字串,第一反…...

Jetpack Compose 和 Compose Multiplatform 还有 KMP 的关系
今天刚好看到官方发布了一篇文章,用于讨论 Compose Multiplatform 和 Jetpack Compose 之间的区别,突然想起之前评论区经常看到说 “Flutter 和 CMP 对于 Google 来说项目重叠的问题”,刚好可以放一起聊一聊。 最近写的几篇内容写的太干&…...
微信小程序中实现背景图片完全覆盖显示,可以通过设置CSS样式来实现
wxml页面代码 <view class"beijing"></view>wxss样式代码 /* pages/beiJing/beiJing.wxss */ .beijing {background-image: url("https://www.qipa250.com/qipa.jpg");/* 定位:绝对定位 */position: absolute;/* 上下左右都定位到…...

【0x0012】HCI_Delete_Stored_Link_Key命令详解
目录 一、命令参数 二、命令格式及参数 2.1. HCI_Delete_Stored_Link_Key 命令格式 2.2. BD_ADDR 2.3. Delete_All 三、生成事件及参数 3.1. HCI_Command_Complete事件 3.2. Status 3.3. Num_Keys_Deleted 四、命令执行流程 4.1. 命令发送阶段 4.2. 控制器处理阶段…...

console的各种方法
console除了常用的log方法,还有很多方便的方法。 console.table 表格 将数据以表格形式展示 console.group 分组 console.group、console.groupEnd:开启、结束分组,使结构更加清晰 console.dir 对象 打印函数或dom时,log无法打…...

spring boot关于系统首页自动跳转拼接到index
业务说明 通过http://localhost:8091访问服务器时,会动态的跳转到系统的欢迎页面. 实现原理: 说明程序启动时会自动的加载一个默认的请求路径(url:http://localhost:8091/) index 之后动态的拼接前缀和后缀. /WEB-INF/views/index.jsp...
指针生成网络(PGN)详细指南(引入)
一、Seq2Seq模型:编码-解码框架的开山之作 我们首先要了解的是seq2seq(Sequence-to-Sequence)模型。它最早由Google在2014年的一篇论文中提出,是第一个真正意义上的端到端的编码器-解码器(Encoder-Decoder)…...

案例研究丨浪潮云洲通过DataEase推进多维度数据可视化建设
浪潮云洲工业互联网有限公司(以下简称为“浪潮云洲”)成立于2018年,定位于工业数字基础设施建设商、具有国际影响力的工业互联网平台运营商、生产性互联网头部服务商。截至目前,浪潮云洲工业互联网平台连续五年入选跨行业跨领域工…...

k8s 蓝绿发布、滚动发布、灰度发布
在Kubernetes(k8s)中,蓝绿发布、滚动发布、灰度发布(金丝雀发布)是三种常见的应用部署和更新策略。下面将分别对这几种发布方式进行说明,并给出相应的例子。 蓝绿发布 蓝绿发布是一种无缝切换版本的部署策…...
UWB原理:AOA测角原理Angel of Arrival
AOA测角原理Angel of Arrival 准备工作: UWB默认使用channel=9,Frequency = 7987.2mMhz,约8GHz。 波长 天线RX1, RX2间距一般为20mm左右,假如发射端TX离2个RX距离2m=2000mm,大约是100倍天线间距。2个入射角可以近似相同。 测角原理: <...
plus.runtime.install在android10无效
在 Android 10 中,使用 plus.runtime.install 方法来进行动态安装应用或进行其他操作可能会失效。这是因为从 Android 10 开始,操作系统在安全性和隐私方面做了很多改进,特别是与应用安装相关的权限变更。 在 Android 10(API 级别…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...