多线程之线程安全问题

news/2025/2/12 19:55:29/

1.线程安全示例

class Count{int a = 0;public void add(){a++;}
}
public class ThreadDemo8 {public static void main(String[] args) {Count count = new Count();Thread t1 = new Thread(()->{for (int i = 0; i < 5_0000; i++) {count.add();}});Thread t2 = new Thread(()->{for (int i = 0; i < 5_0000; i++) {count.add();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count.a);}
}

对于变量a是希望通过两个for循环自增到100000,但是运行结果达不到预期:

 

每一次的运行结果都是不稳定的,这就是多线程到来的问题。对上述问题进行图解(load、add、save):

对于自增操作,本质上要分为三步:

1.先把内存中的值,读取到CPU的寄存器中(load)

2.把 CPU寄存器里的数值进行+1运算(add)

3.把得到的结果写会到内存中(save)

如果是两个线程并发的执行count++,此时就相当于两组load add save进行执行.此时不同的线程调度顺序就可能会产生一些结果上的差异。

2.原因分析

线程安全问题的原因有如下:

1.根本原因:线程都是抢占式执行的,随机调度的。

2.代码结构原因:多个线程同时修改同一个变量。在有些时候,是可以通过调整代码结构来避免的。一个线程修改一个变量是没问题的,多个线程读取同一个变量也是没有问题的,多个线程修改不同的变量也是没问题的。

3.原子性:如果修改操作是原子性的,就可以避免了,比如把上述的load、add和save包装成一个整体就是ok的。针对线程安全问题,最主要的解决手段也是从原子性这个方面进行入手的。

4.内存可见性问题:如果一个线程读,一个线程修改,那么此时读取的结果可能不如预期值。

5.指令重排序:本质上是编译器优化出bug了,编译器有些时候会保证在代码逻辑不变的情况下对代码进行调整,那么有可能会把代码的执行顺序给调整了,很可能会带来问题。

3.如何解决?

用synchronized进行加锁:

进了方法就会加锁,出了方法就会解锁。如果是多个线程同时都在尝试加锁,那么最终只会有一个线程加锁成功,其他线程只能阻塞等待(BLOCKED)。加锁的本质就是把并发变成串行。加锁之后的运行结果:

4.关于synchronized的用法

1.修饰方法

修饰方法包括了修饰修饰普通方法和修饰静态方法,这两者的加锁对象是不同的。修饰普通方法,锁对象就是this;修饰静态方法,锁对象就是类对象(Count1.class)。

2.修饰代码块

修饰代码块就是手动指定锁对象。

总之,在加锁前,我们要明确执行对哪个对象加锁,如果多个线程针对同一个对象加锁,会产生阻塞等待(锁竞争/锁冲突);如果多个线程针对不同对象加锁,不会阻塞等待(不会锁冲突/锁竞争)。


http://www.ppmy.cn/news/9494.html

相关文章

深度学习各子领域略览及术语列表

诸神缄默不语-个人CSDN博文目录 最近更新时间&#xff1a;2023.1.5 最早更新时间&#xff1a;2023.1.5 有监督supervised / 无监督unsupervised分类 多分类multi-class多标签multi-label极限多标签文本分类XMTC&#xff08;NLP课题入门 | 极限多标签文本分类 NLP课题入门 | 极…

【华为OD机试真题2023 JAVA】查找充电设备组合

华为OD机试真题,2023年度机试题库全覆盖,刷题指南点这里 查找充电设备组合 时间限制:5s 空间限制:256MB 限定语言:不限 题目描述: 某个充电站,可提供n个充电设备,每个充电设备均有对应的输出功率。任意个充电设备组合的输出功率总和,均构成功率集合P的1个元素。功率集…

【三】3D匹配Matching之曲面匹配Surface—Based——create_surface_model()算子

&#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; Halcon算子太多&#xff0c;学习查找都没有系统的学习查找路径&#xff0c;本专栏主要分享Halcon各类算子含义及用法&#xff0c;有…

uniapp 填坑之旅---udb微信小程序端显示异常

功能描述&#xff1a;A页面展示列表a&#xff0c;点击a&#xff0c;进入B页面&#xff0c;展示a对象关联的子对象b。在B页面中&#xff0c;通过unicloud-db组件manual模式加载&#xff0c;具体代码按照官网示例来写。问题描述&#xff1a;代码实现后&#xff0c;一直在H5调试&a…

Step10.选择静态库或共享库

Step10.选择静态库或共享库 在本节中&#xff0c;我们将展示如何使用BUILD_SHARED_LIBS变量来控制add_library()的默认行为&#xff0c;并允许控制如何构建没有显式类型&#xff08;STATIC、SHARED、MODULE或OBJECT&#xff09;的库。 为了实现这一点&#xff0c;我们需要将B…

【C语言】volatile 关键字

目录一、前言二、C语言中变量的访问1. 读变量2. 写变量三、代码优化1. 硬件层面&#xff1a;2. 软件层面&#xff1a;四、volatile的定义五、volatile的应用场合1. 中断2. 多线程3. 硬件寄存器一、前言 volatile 是 C语言 中规定的一个关键字&#xff0c;C语言课程中很少会提及…

【BSP视频教程】BSP视频教程第25期:CAN/CANFD/CANopen专题,CAN知识点干货分享, 收发执行过程和错误帧处理(2023-01-03)

视频教程汇总帖&#xff1a;【学以致用&#xff0c;授人以渔】2023视频教程汇总&#xff0c;DSP第10期&#xff0c;ThreadX第5期&#xff0c;BSP驱动第25期&#xff0c;USB实战第5期&#xff0c;GUI实战第3期&#xff08;2023-01-03&#xff09; - STM32F429 - 硬汉嵌入式论坛 …

一些加速库Blas OpenMP等

一些加速库Blas OpenMP等 一、OpenMP1.1 多执行绪的概念1.2 多执行绪的程式1.3 OpenMP 的基本使用1.4 OpenMP使用详解二、MPI (Message Passing Interface)三、 CUDA3.1 CUDA发展历程3.2 CUDA体系结构3.3 CUDA工具包3.4 nvcc C语言编译器3.5 CUDA的运算3.6 GPU并行计算过程一、…