线程安全问题的原因及解决方案

2023-09-16 22:28:57

                要想知道线程安全问题的原因及解决方案,首先得知道什么是线程安全,想给出一个线程安全的确切定义是复杂的,但我们可以这样认为:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。例如:使用两个线程分别对同一个变量进行修改,得出的结果与使用一个线程对这个变量进行修改的结果不同,这样的问题就可以说是该程序不是线程安全的。知道了什么是线程安全后,这样才好分析线程安全问题的原因及解决方案。

        原因1)多个线程之间的调度顺序是随机的,操作系统使用抢占式策略来执行线程(根本原因),并且该原因无法改变,当前主流的操作系统都是如此:例如当两个线程分别对同时一个变量count++,则会使每次得到的结果不同,因为CPU的调度是抢占式的,且count++实际上有着三步操作,这就将导致得到的结果不同。因为count++的三步操作为:

1. 从内存把数据读到 CPU
2. 进行数据更新
3. 把数据写回到 CPU
因此当多个线程进行count++时,就会导致如线程1刚进行完操作1后,线程2抢占了CPU,使得线程1没有及时将数据更新并将数据写回到CPU上,所以线程2读的数据与线程1读的数据相同,因此它们将数据更新后并写回到CPU也是相同,因此相当于count只进行了一次count++,
它们之中的顺序是任意的,因此得到的结果也是不确定的,但一定小于原本要得到的值(count++分别在多个线程中进行了多次)。
        原因2)多个线程同时修改同一个变量,容易产生线程安全问题。可以通过调整代码结构进行避免。
        原因3)修改操作不是原子的:原子性则是不可再分,如count++,可以分为三步操作。也可通过代码来进行封装成原子的来解决,也就是通过锁来进行互斥,使得有个线程操作时,别的线程不能进行操作。解决方法通常是加锁。
        原因4)内存可见性引起的线程安全问题。当判断条件一个线程里面没有改变的话,那么编译器就会进行优化,令其只进行一次判断,后续就不再判断,倘若在另一个线程将其条件改变的话,但再这个线程里不会感觉的到,因此会继续按照之前的判断来进行。解决方法通常是对其条件进行volatile来进行修饰。
public static volatile int count = 0;//倘若没有volatile,则该代码会一直进行下去
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        while(count == 0){
            ;
        }
    });
    Thread t2 = new Thread(() -> {
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count = 1;
    });
    t1.start();
    t2.start();
    t1.join();
}
        原因5)指令重排序引起的线程安全问题。
上述的例子的线程安全问题主要有原因1,2,3组成,因此,若想解决,我们只需给count++进行加锁就行。这样就使的count++操作变成原子的,当count++时,倘若又有一个线程要进行count++,就会产生阻塞,知道先进行count++的线程结束,另一个线程才能进行count++操作。
通常来说,大部分解决线程安全问题,只需要进行加锁就行。
更多推荐

模板学堂|数据可视化仪表板大屏设计流程梳理

DataEase开源数据可视化分析平台于2022年6月正式发布模板市场(https://dataease.io/templates/)。模板市场旨在为DataEase用户提供专业、美观、拿来即用的仪表板模板,方便用户根据自身的业务需求和使用场景选择对应的仪表板模板,并在优质模板的基础上轻松制作自己的仪表板。目前,Dat

可视化图表组件之股票数据分析应用

股市是市场经济的必然产物,在一个国家的金融领域之中有着举足轻重的地位。在过去,人们对于市场走势的把握主要依赖于经验和直觉,往往容易受到主观因素的影响,导致决策上出现偏差。如今,通过数据可视化呈现,便可将历年数据和市场情报进行深度挖掘、分析,从中找到规律和趋势,帮助用户做出更准确的判断。回顾2022年A股市场的表现可谓是

Python爬虫从端到端抓取网页

网页抓取和RESTAPI简介网页抓取是使用计算机程序以自动方式从网站提取和解析数据的过程。这是创建用于研究和学习的数据集的有用技术。虽然网页抓取通常涉及解析和处理HTML文档,但某些平台还提供RESTAPI来以机器可读格式(如JSON)检索信息。在本教程中,我们将使用网络抓取和RESTAPI创建真实的数据集。如何运行代

Git学习笔记1

任务要求:1、使用git提交代码到仓库;2、实现自动代码发布系统;1、了解DevOps的发展历程和思想;2、学会git版本控制;3、会使用github公有仓库和gitlab私有仓库;4、了解CI/CD;5、使用jenkins实现自动发布;DevOps是一种实现Dev(开发)与Ops(运维)工作流有效联合的思想。最终目标

深入探索OCR技术:前沿算法与工业级部署方案揭秘

深入探索OCR技术:前沿算法与工业级部署方案揭秘注:以上图片来自网络1.OCR技术背景1.1OCR技术的应用场景OCR是什么OCR(OpticalCharacterRecognition,光学字符识别)是计算机视觉重要方向之一。传统定义的OCR一般面向扫描文档类对象,现在我们常说的OCR一般指场景文字识别(SceneT

服务器数据恢复-热备盘同步过程中硬盘离线的RAID5数据恢复案例

服务器数据恢复环境:华为OceanStor某型号存储,11块硬盘组建了一组RAID5阵列,另外1块硬盘作为热备盘使用。基于RAID5阵列的LUN分配给linux系统使用,存放Oracle数据库。服务器故障:RAID5阵列1块硬盘由于未知原因离线,热备盘激活开始同步数据,在热备盘同步的过程中又有1块硬盘离线,RAID5阵

一起学数据结构(7)——树及二叉树的基本概念及存储

前面的关于数据结构的文章中,介绍了顺序表,链表,栈,队列等数据结构。对于以上数据结构,均是一对一的关系。本篇文章将对于一对多的数据结构——树进行解析。目录1.树的定义及基本概念:1.1树的定义:1.2树的基本概念及术语:2.树的存储:3.二叉树的概念及结构:3.1二叉树的概念:3.2两种特殊的二叉树:3.2.1满二叉树

Learn Prompt-ChatGPT 精选案例:写作&博客

在ChatGPT的帮助下,文本内容的产出,尤其是撰写博客文章的过程得到了进一步的简化。你可以让ChatGPT激发你的灵感,也可以让它美化你的文章内容。这里我们希望能通过prompt写出一篇以“ChatGPT对社会各行各业的影响”为主题的博客。本页我们希望你可以使用ChatGPT网页版,利用ChatGPT写作的过程应该是

「聊设计模式」之模板方法模式(Template Method)

🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!前言在软件开发中,设计模式是经典的解决方案,它们被广泛应用于面向对象的程序设计中。其中,模板方法模式(TemplateMethod)是一种常用的行为型设计模式,它定义一个操作中的算法骨架,而将一些步骤延迟

推进“数智+数治”,中期科技智慧公厕驱动城市公厕更新升级发展

随着城市化的快速发展和人口的不断增加,公共厕所这一基础设施的更新升级成为了亟待解决的问题。过去的传统公厕往往存在着环境脏乱差、无法保证使用者的舒适度等诸多问题。而智慧公厕则能够通过互联网和物联网的技术手段,实现智能化的运行管理,为市民提供更加便捷、舒适的使用体验。如中期科技「智慧公厕-智慧厕所」,拥有厕位智能监测与引导

【Python深度学习】深度学习框架Tensorflow、Pytorch介绍

深度学习已经成为了人工智能领域的一股重要力量,而深度学习框架则是在这个领域中进行研究和应用的必备工具。常见的深度学习框架包括TensorFlow、PyTorch、Keras、Theano和Caffe等,其中TensorFlow和PyTorch是最受欢迎的两个框架。本文将着重介绍这两个框架的优缺点以及应用领域。1.Ten

热文推荐