线程安全问题 绝地灬酷狼 2024-04-23 20:34 103阅读 0赞 > ⭐ 作者:小胡\_不糊涂 > ? 作者主页:[小胡\_不糊涂的个人主页][Link 1] > ? 收录专栏:[JavaEE][] > ? 持续更文,关注博主少走弯路,谢谢大家支持 ? #### 线程安全 #### * 1. 产生线程不安全的原因 * * 1.1 修改共享数据 * 1.2 内存可见性问题 * 1.3 原子性问题 * 1.4 指令重排序问题 * 2. 解决办法 * * 2.1 加锁 * 2.2 加volatile ![在这里插入图片描述][8f8ef418dd5a4a7c8c5ce81715616a56.gif_pic_center] ## 1. 产生线程不安全的原因 ## 线程的调度是随机的,随机调度会使一个程序在多线程环境下,执行顺序存在很多的变数,此时就需要我们保证代码在任意执行顺序下,都能正常工作。 ### 1.1 修改共享数据 ### 多个线程修改同一个变量。 下面的代码中,涉及到多个线程针对 count 变量进⾏修改。此时这个 count 是⼀个多个线程都能访问到的 “共享数据”。 public class TestDemo1 { private static int count =0; public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(()->{ for(int i=0;i<5000;i++){ count++; } }); Thread t2=new Thread(()->{ for(int i=0;i<5000;i++){ count++; } }); t1.start(); t2.start(); //保证t1,t2自增完后再打印 t1.join(); t2.join(); System.out.println("count="+count); } } ![在这里插入图片描述][1b7dbbe3a9ec4612ba1f26aea05e3168.png] 按照正常的思路来讲,t1自增5000,t2自增5000,count最终应该是10000,但为什么真正的输出跟我们想的不一样呢? ### 1.2 内存可见性问题 ### **可⻅性**指⼀个线程对共享变量值的修改,能够及时地被其他线程看到。 **Java 内存模型 (JMM):** Java虚拟机规范中定义了Java内存模型。 ⽬的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到⼀致的并发效果。 ![在这里插入图片描述][fc247f8dce0a4eba81e8c98e05817979.png] * 线程之间的共享变量存在主内存 (Main Memory) * 每⼀个线程都有⾃⼰的 “⼯作内存” (Working Memory) * 当线程要读取⼀个共享变量的时候,会先把变量从主内存拷⻉到⼯作内存,再从⼯作内存读取数据 * 当线程要修改⼀个共享变量的时候,也会先修改⼯作内存中的副本,再同步回主内存 由于每个线程有⾃⼰的⼯作内存,这些⼯作内存中的内容相当于同⼀个共享变量的 “副本”。此时修改线程1的⼯作内存中的值,线程2的⼯作内存不⼀定会及时变化。 **这里的"主内存"是真正硬件角度的"内存"。⽽所谓的 “工作内存”,则是指 CPU 的寄存器和高速缓存** 例如: 1.初始情况下,两个线程的工作内容都是一样的 ![在这里插入图片描述][2abba77e6546491db5aba98fca217ddf.png] 2.⼀旦线程1修改了 a 的值,此时主内存不⼀定能及时同步。对应的线程2的⼯作内存的 a 的值也不⼀定能及时同步 ![在这里插入图片描述][bc766f6e3e924396a92fda26e7675920.png] 此时代码就容易出现问题。 ### 1.3 原子性问题 ### **什么是原⼦性?** 我们把⼀段代码想象成⼀个房间,每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证,A进⼊房间之后,还没有出来;B 是不是也可以进⼊房间,打断 A 在房间⾥的隐私。这个就是不具备原⼦性的。 > 那我们应该如何解决这个问题呢?是不是只要给房间加⼀把锁,A 进去就把⻔锁上,其他⼈是不是就进不来了。这样就保证了这段代码的原⼦性了。 > 有时也把这个现象叫做同步互斥,表⽰操作是互相排斥的。 **如果不保证原子性会给多线程带来什么问题?** 如果⼀个线程正在对⼀个变量操作,中途其他线程插⼊进来了,如果这个操作被打断了,结果就可能是错误的。 ### 1.4 指令重排序问题 ### ⼀段代码的逻辑是这样的: 1. 去前台取下 U 盘 2. 去教室写10分钟作业 3. 去前台取下快递 如果是在单线程情况下,JVM、CPU指令集会对其进⾏优化,⽐如,按 1->3->2的⽅式执⾏,也是没问题,可以少跑⼀次前台。这种叫做**指令重排序** > 编译器对于指令重排序的前提是 “保持逻辑不发⽣变化”。这⼀点在单线程环境下⽐较容易判断,但是在多线程环境下就没那么容易了,多线程的代码执⾏复杂程度更⾼,编译器很难在编译阶段对代码的执⾏效果进⾏预测,因此激进的重排序很容易导致优化后的逻辑和之前不等价。 ## 2. 解决办法 ## ### 2.1 加锁 ### synchronized 能够保证原⼦性,解决互斥问题。 public class TestDemo1 { private static int count =0; public static void main(String[] args) throws InterruptedException { Object loker=new Object(); Thread t1=new Thread(()->{ for(int i=0;i<5000;i++){ synchronized (loker) { count++; } } }); Thread t2=new Thread(()->{ for(int i=0;i<5000;i++){ synchronized (loker) { count++; } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("count="+count);//10000 } } 当t1线程拿到锁(loker)后开始count自增,t2线程则进入阻塞等待,等t1解锁后,t2获得锁,count再进行自增,就避免了因看不见内存中的变化而产生的错误。 ### 2.2 加volatile ### volatile关键字的作用主要有如下两个: 1. 保证内存可见性:基于屏障指令实现,即当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。 2. 保证有序性:禁止指令重排序。编译时 JVM 编译器遵循内存屏障的约束,运行时靠屏障指令组织指令顺序。 注意: volatile 不能保证原子性 [Link 1]: https://blog.csdn.net/iLoyo_ [JavaEE]: http://t.csdnimg.cn/p0IQF [8f8ef418dd5a4a7c8c5ce81715616a56.gif_pic_center]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/23/680247961e4d4f3c897bb1e3918fa0e5.gif [1b7dbbe3a9ec4612ba1f26aea05e3168.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/23/57ecaa95285a4bd0a5ffbdff80fc78bf.png [fc247f8dce0a4eba81e8c98e05817979.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/23/8409047d75d74937a724eaf4aa74eefe.png [2abba77e6546491db5aba98fca217ddf.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/23/1d94037dd1e943d0b5230ec66cf51688.png [bc766f6e3e924396a92fda26e7675920.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/23/8eef0ef1efd448e7bc47028fe9e0af26.png
相关 线程安全问题 我们把⼀段代码想象成⼀个房间,每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证,A进⼊房间之后,还没有出来;B 是不是也可以进⼊房间,打断 A 在房间⾥的隐私。这个就... 绝地灬酷狼/ 2024年04月23日 20:34/ 0 赞/ 104 阅读
相关 线程安全问题 作者简介: zoro-1,目前大二,正在学习Java,数据结构,mysql,javaee等作者主页:??。 朴灿烈づ我的快乐病毒、/ 2024年04月20日 04:48/ 0 赞/ 116 阅读
相关 线程安全问题 目录 1.不安全原因 1.1线程调度无序(抢占式) 1.2多个线程修改同一个变量 1.3修改操作不是原子的 1.4内存可见性 1.5指令重排序 2.解决问题 2 缺乏、安全感/ 2024年03月25日 18:16/ 0 赞/ 81 阅读
相关 线程安全问题 一、线程安全 VS 线程不安全? 线程安全指的是代码若是串行执行和并发执行的结果完全一致,就称为该代码是线程安全的。 若多个线程串行执行(单线程执行)的结果和并发执行的 短命女/ 2023年10月15日 17:32/ 0 赞/ 84 阅读
相关 线程安全问题 定义 > 首先大家需要思考一下何为线程安全性呢??? 《Java并发编程实战》书中给出定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替 旧城等待,/ 2023年10月01日 19:02/ 0 赞/ 45 阅读
相关 线程安全问题 ![在这里插入图片描述][f9d8ad49a2a449b9888b716a9946db5c.gif_pic_center] 文章目录 一、线程安全问题 二、 逃离我推掉我的手/ 2023年09月27日 13:11/ 0 赞/ 35 阅读
相关 线程安全问题 基本概述: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nL àì夳堔傛蜴生んèń/ 2022年11月29日 12:21/ 0 赞/ 259 阅读
相关 线程安全问题 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污 迷南。/ 2022年08月21日 04:15/ 0 赞/ 283 阅读
相关 线程安全问题 线程安装概念 当多个线程,访问某一个类,对象或者方法时 这个类始终都能表现出正确的行为 那么,这个类,对象或者方法,就是线程安全的 Synchronize 朴灿烈づ我的快乐病毒、/ 2022年05月17日 08:06/ 0 赞/ 305 阅读
相关 线程安全问题 a++造成运行结果错误 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly ╰半橙微兮°/ 2021年09月24日 02:46/ 0 赞/ 478 阅读
还没有评论,来说两句吧...