多线程(四)----线程安全

devtools/2025/3/19 7:23:09/

线程安全问题的万恶之源就是多线程的抢占式执行所带来的随机性.

有了多线程, 此时抢占式执行下, 代码执行的顺序, 会出现更多的变数, 代码执行顺序的可能性就从一种情况变成了无数种情况. 只要有一种情况使得代码结果不正确, 都是视为bug, 线程不安全.

有线程安全的代码

以下是一个有线程安全的的代码:

java">class Counter{public int count;public void add(){count++;}
}public static void main(String[] args) throws InterruptedException {Counter counter = new Counter();//搞两个线程, 两个线程分别针对counter 来调用5w次的add方法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();//等待两个线程结束try{t1.join();t2.join();} catch (InterruptedException e){e.printStackTrace();}//打印最终的 count 值System.out.println("count = "+counter.count);}

代码结果:

从代码结果可以知道, 并不是和预期一样, 达到10w次.

为什么会出现这种情况呢?

原因就出现在 "count++" 这里

"++" 操作本质上要分成三步(类似于汇编)

第一步: 先把内存中的值, 读取到CPU的寄存器中 (load)

第二部: 把CPU寄存器里的数值进行 "+1" 运算 (add)

第三步: 把得到的结果写回到内存中 (save)

如果是两个线程并发执行, 此时就相当于两组load add save 进行执行, 此时不同的线程调度顺序就可能会产生一些结果上的差异.(将相当于6个操作, 可能出现的排列次序不同)

这是由于线程之间是随机调度的, 导致此处的调度顺序充满着其他的可能性

脏读

由于两个核心上处理同一个内存上的变量, 如果是按顺序完成自身的任务(load add save)后, 另一个线程在开始自增, 那么结果肯定是10w次, 但是由于随机调度, 就会出现一个线程自增后, 还没有保存, 而另一个线程已经读取的情况, 就会导致内存上只++1次, 没有按预期一样++2次.

线程安全问题的主要原因

线程安全问题的主要原因:

1. [根本原因] 抢占式执行, 随即调度,

2. 代码结构: 多个线程同时修改同一个变量

    一个线程修改一个变量不会出现安全问题

    多个线程读取同一个变量不会出现安全问题(例如:String是不可变对象, 天然是线程安全的)

    多个线程修改多个不同的变量不会出现安全问题

3.原子性:  如果修改操作是非原子的, 就会容易出现线程安全问题

    原子性: 比如汇编中的一条指令,或者说几条指令互不影响

    针对线程安全问题的解决, 最主要的手段就是从原子性入手, 把非原子的操作, 变为原子的

4. 内存可见性: 如果一个线程读, 一个线程修改 也会出现安全问题

5. 指令重排序: 编译器主动调整你的代码

线程安全问题的解决----Synchronized

java">//synchronize 是一个关键字, 表示加锁
synchronized public void add(){count++;
}

加了synchronized之后, 进入方法就会加锁, 出了方法就会解锁, 如果两个线程同时尝试加锁, 此时一个能获取锁成果, 另一个只能阻塞等待(BLOCKED) 一直阻塞到线程释放锁(解锁), 当前线程才能加锁成功.

加锁, 就是保证原子性, 加锁的本质是把并行, 变成了串行

加锁之后, 代码结果就变成了10w次

synchronized关键字----监视器monitor lock

synchronized 使用方法

1.修饰方法

  1) 修饰普通方法; 锁对象就是this

java">public synchronized void add(){count++;
}

直接把synchronized 修饰到方法上了, 此时相当于对 this 加锁.

java">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 执行 add 就加上锁了, 针对count这个对象加上锁了, t2 执行 add 的时候, 也尝试对counter加锁, 但是由于counter 已经被 t1 给占用了, 因此这里的加锁操作就会阻塞.

  2)修饰静态方法: 锁对象就是类对象(Counter,class)

     与1) 同理

2.修饰代码块

显式/手动指定锁对象

java">public void add(){synchronized (this){count++;}
}

  "( )" 内可以指定任意想指定的对象, 不一定非是 this, 进了代码块就加锁, 出了代码块就解锁

synchronized 的特性

1) 互斥

  进入 synchronized 修饰的代码块, 相当于 加锁

  推出synchronized 修饰的代码块, 相当于解锁

加锁

如果两个线程针对同一个对象进行加锁, 就会出现锁竞争/锁冲突, 一个线程能够获取到锁(先到先得)另一个线程阻塞等待, 等待到上一个线程解锁, 它才能获取锁成功

如果两个线程针对不同对象加锁, 此时不会发生锁竞争/锁冲突. 这俩线程都能获取到各自的锁, 不会阻塞等待了.

2) 可重入

synchronized 同步块对同一条线程来说是可重入的, 不会出现自己把自己锁死的问题.

Java标准库中的线程安全类

 

Java标准库中很多都是不是线程安全的. 例如以上, 这些类可能会涉及到多线程修改共享数据, 有没有任何加锁措施

还有一些是线程安全的, 使用了一些锁机制来控制

还有的虽然没加索, 但是不涉及"修改", 仍然是线程安全的-----String


http://www.ppmy.cn/devtools/168277.html

相关文章

HarmonyOS三层架构实战

目录&#xff1a; 1、三层架构项目结构1.0、三层架构简介1.1、 common层&#xff08;主要放一些公共的资源等&#xff09;1.2、 features层&#xff08;主要模块定义的组件以及图片等静态资源&#xff09;1.3、 products层&#xff08;主要放主页面层和一些主要的资源&#xff…

论文精度:Transformers without Normalization

前言 论文题目:Transformers without Normalization 作者:Jiachen Zhu 1,2 , Xinlei Chen 1 , Kaiming He 3 , Yann LeCun 1,2 , Zhuang Liu 1,4,† 论文地址:https://arxiv.org/pdf/2503.10282 摘要 这篇论文探讨了现代神经网络中广泛使用的归一化层是否是必不可少的。…

分区表和分表

分区表&#xff08;Partitioning&#xff09; 定义 分区表是将单个表的数据按照某种规则&#xff08;如范围、列表、哈希等&#xff09;划分为多个逻辑部分&#xff0c;每个部分称为一个分区。数据仍然存储在一个物理表中&#xff0c;但逻辑上被分割为多个分区。 特点 逻辑…

20250318在ubuntu20.04中安装向日葵

rootrootrootroot-X99-Turbo:~$ sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb rootrootrootroot-X99-Turbo:~$ sudo apt-get install -f rootrootrootroot-X99-Turbo:~$ sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb 20250318在ubuntu20.04中安装向日葵 2025/3…

自定义uniapp组件,以picker组件为例

编写目的 本文说明基于vue3定义uniapp组件的关键点&#xff1a; 1、一般定义在components文件夹创建组件&#xff0c;组件与页面已经没有明确的语法格式区别&#xff0c;所以可以与页面的语法保持一致 &#xff1b; 2、组件定义后使用该组件的页面不需要引用组件即可使用&am…

华为手机新品将采用新屏幕形态,3月20日揭晓谜底

在科技飞速发展的当下,智能手机市场的竞争可谓白热化。各大厂商不断推陈出新,试图在这片红海之中抢占更多份额。而华为,作为其中的佼佼者,一直以创新为驱动,致力于为消费者带来前所未有的体验。年初,华为常务董事、终端BG董事长、智能汽车解决方案BU董事长余承东在社交媒…

深度学习-简介

一、几个概念 &#xff08;1&#xff09;what is ai including? 看一张图&#xff1a; 这里注意机器学习和深度学习的关系 &#xff08;2&#xff09;机器学习和模式识别有什么区别&#xff1f; 和机器学习同领域的有一个词叫做模式识别&#xff0c;二者有什么区别呢? 机…

【arXiv 2025】卷积加法自注意力CASAtt,轻量且高效,即插即用!

一、论文信息 论文题目&#xff1a;CAS-ViT: Convolutional Additive Self-attention Vision Transformers for Efficient Mobile Applications 中文题目&#xff1a;CAS-ViT&#xff1a;用于高效移动应用的卷积加法自注意力视觉Transformer 论文链接&#xff1a;https://a…