线程安全问题 缺乏、安全感 2024-03-25 18:16 70阅读 0赞 **目录** 1.不安全原因 1.1线程调度无序(抢占式) 1.2多个线程修改同一个变量 1.3修改操作不是原子的 1.4内存可见性 1.5指令重排序 2.解决问题 2.1synchronized 关键字 2.1.1 特点 2.1.2 synchronized的过程 2.2使用 2.2volatile 关键字 -------------------- ## 1.不安全原因 ## ### 1.1线程调度无序(抢占式) ### public static void main(String[] args) throws InterruptedException { Counter counter=new Counter(); //两个线程,分别对count自增5w次 Thread t1=new Thread(()->{ for (int i = 0; i < 50000; i++) { counter.add(); } }); Thread t2=new Thread(()->{ for (int i = 0; i < 50000; i++) { counter.add(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(counter.get()); } 实际结果和预期结果不相符==>线程不安全 ### ### 原因:与线程调度随机性相关 count++:由3个cpu指令构成 1.load,把内存中的数据读取到cpu寄存器中 2.add,寄存器中值,进行+1运算 3.save,把寄存器中的值写回到内存中 由于多线程调度顺序不确定,实际执行过程中,++操作的指令顺序有很多可能 例如: 第一种: t1.load t1.add t1.save t2.load t2.add t2.save 执行过程:t1和t2可能在同一个cpu核心,可能在不同的cpu核心 t1=0 t1中++ -->t1=1 内存中count=1 t2=1 t2中++ -->t2=2 内存中count=2 第二种: 执行过程: t1.load t2.load t2.add t2.save t1.add t1.save t1=0 t2=0 t2中++ t2=1 内存中count=1 t1中++ t1=1 内存中count=1 两个结果不同 当前调度顺序无序,不知道有多少次是顺序执行,多少次是交错执行,所以每次结果都不一样 ### 1.2多个线程修改同一个变量 ### 一个线程修改同一个变量==>安全 多个线程读取同一个变量==>安全 多个线程修改不同变量==>安全 ### 1.3修改操作不是原子的 ### 不可分割的最小单位是原子 例如: 1. 像++操作不是原子的,可以拆分成3个操作(load,add,save) 如果操作对应多个cpu指令,大概率不是原子 2. 如果是直接使用=赋值,就是原子操作 因为不是原子导致两个线程的指令排列存在变数 不是原子导致两个线程的指令排列存在变数,导致执行结果与预期结果不同 ### 1.4内存可见性 ### 内存可见性:多线程环境下,编译器对代码优化,产生了误判,引发了bug 例如: while(flag==0){ } while循环,flag==0 load 从内存读取数据到cpu寄存器,cmp比较寄存器值是否为0 load时间开销高于cmp,因为load开销大,load结果一样,所以编译器就把load优化掉了,只有第一次执行load真正执行了(编译器优化的手段) 编译器优化,在程序结果变前提下,通过加减语句,变换语句,列操作,让整个程序执行的效率大大提升在程序结果不变前提下:单线程下判定是非常准确,多线程就不一定了 ### 1.5指令重排序 ### 指令重排序,是编译器优化的策略 调整代码执行顺序,让程序更高效 调整代码执行顺序,单线程不会有影响,但是多线程就不一定了 ### ### ## 2.解决问题 ## ### **2.1synchronized** **关键字** ### #### 2.1.1 特点 #### 某个线程执行到某个对象的 synchronized 中时 , 其他线程如果也执行到 同一个对象 synchronized 就会 **阻塞等待** 进入 synchronized 修饰的代码块 , 相当于 **加锁** 退出 synchronized 修饰的代码块 , 相当于 **解锁** 例如: public void add(){ synchronized (this){ count++; } } 这里的this是锁对象 如果两个线程,针对同一个对象加锁,就会出现锁竞争(一个先拿到锁,另一个阻塞等待) 如果两个线程,针对不同对象加锁,不会有影响 ()里的锁对象,可以写作任意一个Object对象(基本数据类型不行),此处的this,相当于counter Thread t1=new Thread(()->{ for (int i = 0; i < 50000; i++) { counter.add(); } }); Thread t2=new Thread(()->{ for (int i = 0; i < 50000; i++) { counter.add(); } }); 这两个线程在竞争同一个锁对象,就会产生锁竞争,(t1拿到锁,t2阻塞) > 运行结果:100000 此时,线程就安全了 就count++为例: 两个线程t1,t2原本有3个指令,加上synchronized后,加上lock和unlock. 如果t1先执行,保证,t2的load一定在t1的save之后,此时就线程安全 加锁:本质是吧并发的变成串行的 join和加锁的区别: join:两个线程完整的进行串行 加锁:两个线程的某个小部分串行,大部分并行 上述代码,线程的步骤: 1.创建i 2.判定i<5000 3.调用add 4.count++ 5.add返回 6.i++ 只有4是串行,12356是并发,在保证线程安全的前提,同时代价更快,更好利用多核cpu 因为t1还没有执行完count++,此时t2也执行到count++,t2就会阻塞等待,此时t2暂时还不能执行5 6,但是1 2 3都是并发执行.t1,t2都执行完4,5 6 t1 t2依然可以并发执行 加锁可能会阻塞,对效率有影响 比不加锁准确,比串行快 #### 2.1.2 synchronized的过程 #### 1. 获得互斥锁 2. 从主内存拷贝变量的最新副本到工作的内存 3. 执行代码 4. 将更改后的共享变量的值刷新到主内存 5. 释放互斥锁 ### 2.2使用 ### 1. 直接修饰普通方法: 锁的 SynchronizedDemo 对象 public synchronized void methond() { } 2) 修饰静态方法: 锁的 SynchronizedDemo 类的对象 public synchronized static void method() { } 3) 修饰代码块: 明确指定锁哪个对象 锁当前对象 public void method() { synchronized (this) { } 锁类对象 public void method() { synchronized (SynchronizedDemo.class) { } ### **2.2volatile** **关键字** ### volatile 修饰的变量, 能够保证 "内存可见性" volatile public static int flag=0; 每次都从内存读取flag变量的值,编译器不进行优化 volatile 不保证原子性 volatile使用的场景:一个线程读,一个线程写
相关 线程安全问题 我们把⼀段代码想象成⼀个房间,每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证,A进⼊房间之后,还没有出来;B 是不是也可以进⼊房间,打断 A 在房间⾥的隐私。这个就... 绝地灬酷狼/ 2024年04月23日 20:34/ 0 赞/ 91 阅读
相关 线程安全问题 作者简介: zoro-1,目前大二,正在学习Java,数据结构,mysql,javaee等作者主页:??。 朴灿烈づ我的快乐病毒、/ 2024年04月20日 04:48/ 0 赞/ 104 阅读
相关 线程安全问题 目录 1.不安全原因 1.1线程调度无序(抢占式) 1.2多个线程修改同一个变量 1.3修改操作不是原子的 1.4内存可见性 1.5指令重排序 2.解决问题 2 缺乏、安全感/ 2024年03月25日 18:16/ 0 赞/ 71 阅读
相关 线程安全问题 一、线程安全 VS 线程不安全? 线程安全指的是代码若是串行执行和并发执行的结果完全一致,就称为该代码是线程安全的。 若多个线程串行执行(单线程执行)的结果和并发执行的 短命女/ 2023年10月15日 17:32/ 0 赞/ 77 阅读
相关 线程安全问题 定义 > 首先大家需要思考一下何为线程安全性呢??? 《Java并发编程实战》书中给出定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替 旧城等待,/ 2023年10月01日 19:02/ 0 赞/ 29 阅读
相关 线程安全问题 ![在这里插入图片描述][f9d8ad49a2a449b9888b716a9946db5c.gif_pic_center] 文章目录 一、线程安全问题 二、 逃离我推掉我的手/ 2023年09月27日 13:11/ 0 赞/ 16 阅读
相关 线程安全问题 基本概述: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nL àì夳堔傛蜴生んèń/ 2022年11月29日 12:21/ 0 赞/ 249 阅读
相关 线程安全问题 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污 迷南。/ 2022年08月21日 04:15/ 0 赞/ 272 阅读
相关 线程安全问题 线程安装概念 当多个线程,访问某一个类,对象或者方法时 这个类始终都能表现出正确的行为 那么,这个类,对象或者方法,就是线程安全的 Synchronize 朴灿烈づ我的快乐病毒、/ 2022年05月17日 08:06/ 0 赞/ 293 阅读
相关 线程安全问题 a++造成运行结果错误 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly ╰半橙微兮°/ 2021年09月24日 02:46/ 0 赞/ 463 阅读
还没有评论,来说两句吧...