首页IT科技多线程容易出现的问题(多线程的风险 — 线程安全)

多线程容易出现的问题(多线程的风险 — 线程安全)

时间2025-09-19 00:07:35分类IT科技浏览5926
导读:✨个人主页:bit me👇...

✨个人主页: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 的时候    ,仍然可能是一个无效对象!!!

声明:本站所有文章               ,如无特殊说明或标注                        ,均为本站原创发布           。任何个人或组织        ,在未征得本站同意时           ,禁止复制                、盗用                       、采集        、发布本站内容到任何网站            、书籍等各类媒体平台                        。如若本站内容侵犯了原著者的合法权益                        ,可联系我们进行处理            。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
电脑怎样强制关闭所有页面(电脑学习网首发一键强制永久关闭屏Windows11/Windows10/Windows7系统屏蔽自动更新升级功能) 怎样隐藏电脑任务栏游戏窗口(win7如何隐藏任务栏正在游戏的图标? 电脑隐藏任务栏图的技巧)