多线程的风险 --- 线程安全
✨个人主页:bit me👇
✨当前专栏:Java EE初阶👇
✨每日一语:低头赶路,敬事如仪;自知自心,其路则明。
目 录
- 🍸一. 线程不安全
- 🍹二. 线程不安全的原因
🍸一. 线程不安全
多线程编程,最重要,也是最困难的问题就是线程安全问题,它的万恶之源,罪魁祸首就是调度器的随机调度 / 抢占式执行 这个过程
线程不安全:在随机调度之下,程序执行有多种可能,其中的某些可能导致代码出现了 bug ,线程不安全 / 线程安全问题
例如:两个线程对一个变量进行并发的自增(创建俩线程,让这俩线程同时并发的对一个变量,自增 5w 次,最终预期能一共自增 10w 次)
//创建俩线程,让这俩线程同时并发的对一个变量,自增 5w 次,最终预期能一共自增 10w 次
class Counter{//用来保存计数的变量public int count;public void increase(){count++;}
}
public class Demo14 {//这个实例用来进行累加public static Counter counter = new Counter();public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("count: " + counter.count);}
}
运行结果:
再运行一次:
发现随机调度顺序不一样,结果也就不一样
那上面的 bug 如何出现的?
执行一段代码,需要让 CPU 把对应的指令从内存中读取出来,然后再执行
像 count++ 一行代码,对应三个机器指令
- 1.从内存读取数据到 CPU(load)
- 2.在 CPU 寄存器中,完成加法运算(add)
- 3.把寄存器的数据写回到内存中(sava)
指令的排序方式:
上述两种指令的排序方式恰好能到 2 。但是还有许许多多的排列组合方式,就都不一定了。
此时总和就是 1 了
这些还有许许多多就不在此举例了
根据上述的总结,俩种极端情况就是 5w 和 10w 。然后其他的情况就是 5w 和 10w 之间了。
拓展:
操作系统中的随机调度严格意义上来说其实不是 “随机调度” 。在内部他有自己的一套调度过程,我们不需要理解这个过程,了解了也无法改变这个调度。
🍹二. 线程不安全的原因
1. 操作系统的随机调度 / 抢占式执行。
【罪魁祸首,万恶之源】
2. 多个线程修改同一个变量。
【三个条件缺一不可,别的情况都没事儿】(所以写代码中可以针对这三个点进行改变进行规避,但是范围有限,不是所有的场景都可以规避掉)
3. 有些修改操作,不是原子的!
【不可拆分的最小单位,就叫原子】
通过 = 来修改,= 只对应一条机器指令,视为是原子的
通过 ++ 来修改,++ 对应三条机器指令,则不是原子的
什么是原子性?
我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。(后续详解关于锁)
后果:如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。
4. 内存可见性,引起的线程安全问题。
【内存改了,但是在优化的背景下,读不到看不见】
例如一个线程读,一个线程修改:线程 1 LOAD 之后需要进行 TEST ,然后继续 LOAD 再继续 TEST ,这样循环走下去,但是,在程序运行过程中,可能会涉及到一个操作 “优化” (可能是编译器 Javac,也可能是 JVM Java,也可能是操作系统),由于 LOAD 读的操作太慢,反复读,每次读到的数据都是一样的,JVM 就对此做出了优化,不需要重复在内存中读取了,直接就重复用第一次从内存读到寄存器的数据就好了,此时在优化之后,线程 2 突然写了一个数据,由于线程 1 已经优化成读寄存器了,因此线程 2 的修改线程 1 感知不到。
上述优化在单线程环境下没问题,但是多线程情况下就可能产生问题,多线程环境太复杂,编译器/JVM/操作系统进行优化的时候就可能产生误判!!!针对这个问题,Java 引入了 volatile 关键字,让程序猿手动禁止编译器针对某个变量进行上述优化!
5. 指令重排序。
【调整代码执行顺序】
也是编译器 / JVM / 操作系统优化
优化在单线程环境下没问题,但是多线程情况下就可能产生问题,例如 Test t = new Test(); 在底层就有三个步骤:
- 创建内存空间
- 往这个内存空间上构造一个对象
- 把这个内存的引用赋值给 t
此处就容易出现指令重排序引入的问题,2 和 3 的顺序是可以调换的,在单线程下调换这俩是没影响的,多线程下就不行了。例如我们俩线程,第一个线程按照先 2 后 3 的顺序,另一个线程尝试读取 t 的引用,当第二个线程读到 t 为非 null 的时候,此时 t 就一定是一个有效对象!!!如果是按照先 3 后 2 的顺序,第二个线程读到 t 为非 null 的时候,仍然可能是一个无效对象!!!
相关文章:

多线程的风险 --- 线程安全
✨个人主页:bit me👇 ✨当前专栏:Java EE初阶👇 ✨每日一语:低头赶路,敬事如仪;自知自心,其路则明。 目 录🍸一. 线程不安全🍹二. 线程不安全的原因…...

Linux信号详解
文章目录Linux信号什么是信号**从生活角度理解: **技术应用角度的信号进程的注意事项信号概念用kill -l命令可以察看系统定义的信号列表信号处理常见方式概览信号产生通过终端按键产生信号使用signal函数自定义SIGINT信号的处理方式使用sigprocmask函数阻塞2号信号和40号信号vo…...
JAVA使用POI操作EXCEL
设置公式totalRow.createCell(4).setCellFormula("SUM(E9:E35");// 执行公式wb.setForceFormulaRecalculation(true);合并单元格sheet.addMergedRegion(new CellRangeAddress(0, 0, 3, 7));单元格格式CellStyle cellStyle wb.createCellStyle();// 字体XSSFFont fon…...

只做笔记有必要买apple pencil吗?苹果笔的代替笔推荐
如果仅仅使用IPAD来进行打游戏和看剧的话,未免有些浪费。ipad的作用还是挺大的,可以用来做学习笔记,也可以用来做绘画,也可以用来做一些重要的内容。很多人都会认为,苹果的电容笔很好用,但是价格上要比一般…...

Hive---sqoop安装教程及sqoop操作
sqoop安装教程及sqoop操作 文章目录sqoop安装教程及sqoop操作上传安装包解压并更名添加jar包修改配置文件添加sqoop环境变量启动sqoop操作查看指定mysql服务器数据库中的表在hive中创建一个teacher表跟mysql的mysql50库中的teacher结构相同将mysql中mysql50库中的sc数据导出到h…...

【C++】register 关键字
文章目录一. 什么是寄存器?二. 为什么要存在寄存器?三. register 修饰变量一. 什么是寄存器? 我们都知道,CPU主要是负责进行计算的硬件单,但是为了方便运算,一般第一步需要先把数据从内存读取到CPU内&…...

剑指 Offer II 024. 反转链表
题目链接 剑指 Offer II 024. 反转链表 easy 题目描述 给定单链表的头节点 head,请反转链表,并返回反转后的链表的头节点。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例 2: 输入:h…...

从Linux内核中学习高级C语言宏技巧
Linux内核可谓是集C语言大成者,从中我们可以学到非常多的技巧,本文来学习一下宏技巧,文章有点长,但耐心看完后C语言level直接飙升。 本文出自:大叔的嵌入式小站,一个简单的嵌入式/单片机学习、交流小站 从…...
详解Python的装饰器
Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里。 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数。 def say_hello():print "hello!"def say_goodbye():print "hello!" # bug hereif…...

k8s-Pod域名学习总结
k8s-Pod域名学习总结 大纲 k8s内置DNS服务 配置Pod的域名服务 CornDNS配置 默认Pod的域名 自定义Pod的域名 实战需求 1 Pod有自己的域名 2 集群内部的Pod可以通过域名访问其他的Pod 基础准备: 1 k8s 集群版本1.17 k8s内置DNS服务 k8s1.17安装完成后自动创建…...

0405习题总结-不定积分
文章目录1 不定积分的基本概念2 直接积分法-基本积分公式3 第一换元法-凑微分形式法4 第二类换元法5 分部积分求不定积分6 表格法积分7 有理函数求积分后记1 不定积分的基本概念 例1 f(x){x1,x≥012e−x12,x<0求∫f(x)dxf(x) \begin{cases} x1,\quad x\ge0\\ \frac{1}{2}e^…...
QT 常用控件类型命名参考
拟定的QT的控件命名规则:蛇形命名方式 控件类型开头,以下是QT控件类型命名的参考范例 Buttons Buttons起始字符串对象名称举例Push Buttonbuttonbutton_loginTool Buttontool_button / buttonbutton_switchRadio Buttonradio_button / radioradio_boy…...

MATLAB与图像处理的那点小事儿~
目录 一、学习内容 二、matlab基本知识 三、线性点运算 四、非线性点运算,伽马矫正 五、直方图 1、直方图均衡化 (1)使用histep函数实现图像均衡化 (2)使用自行编写的均衡化函数实现图像均衡化 2、直方图规定…...
第十四届蓝桥杯模拟赛(第三期)Java组个人题解
第十四届蓝桥杯模拟赛(第三期)Java组个人题解 今天做了一下第三期的校内模拟赛,有些地方不确定,欢迎讨论和指正~ 文章目录第十四届蓝桥杯模拟赛(第三期)Java组个人题解填空题部分第一题【最小数】第二题【E…...

Go语言之条件判断循环语句(if-else、switch-case、for、goto、break、continue)
一、if-else条件判断语句 Go中的if-else条件判断语句跟C差不多。但是需要注意的是,Go中强制规定,关键字if和else之后的左边的花括号"{“必须和关键字在同一行,若使用了else if结构,则前段代码快的右花括号”}"必须和关…...

深入理解AQS
概念设计初衷:该类利用 状态队列 实现了一个同步器,更多的是提供一些模板方法(子类必须重写,不然会抛错)。 设计功能:独占、共享模式两个核心,state、Queue2.1 statesetState、compareAndSetSta…...

JVM学习笔记十:执行引擎
0. 前言 声明: 感谢尚硅谷宋红康老师的讲授。 感谢广大网友共享的笔记内容。 B站:https://www.bilibili.com/video/BV1PJ411n7xZ 本文的内容基本来源于宋老师的课件,其中有一些其他同学共享的内容,也有一些自己的理解内容。 1. …...

【2023-03-10】JS逆向之美团滑块
提示:文章仅供参考,禁止用于非法途径 前言 目标网站:aHR0cHM6Ly9wYXNzcG9ydC5tZWl0dWFuLmNvbS9hY2NvdW50L3VuaXRpdmVsb2dpbg 页面分析 接口流程 1.https://passport.meituan.com/account/unitivelogin主页接口:需获取下面的参数࿰…...
全志V853芯片放开快启方案打印及在快起方式下配置isp led的方法
全志V85x芯片 如何放开快启方案的打印? 1.主题 如何放开快启方案的打印 2.问题背景 产品:v851系列快启方案 软件:tina 其他:特有版本信息添加自由描述 (如固件版本,复现概率,特定环境&#x…...

大数据 | (一)Hadoop伪分布式安装
大数据原理与应用教材链接:大数据技术原理与应用电子课件-林子雨编著 Hadoop伪分布式安装借鉴文章:Hadoop伪分布式安装-比课本详细 大数据 | (二)SSH连接报错Permission denied:SSH连接报错Permission denied 哈喽&a…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...