多线程编程的安全问题和解决措施

news2024/10/7 17:25:08

线程不安全的概念

由于多线程并发执行,导致结果出错,我们称这种线程是不安全的。

多线程编程出错的原因

一:线程之间并发执行的随机性导致线程不安全

二:多个线程对同一个对象进行修改

三:线程的操作不是原子性的

四:内存可见性问题

五:指令重排序

下面举一个线程不安全的例子:

假设有两个线程A、B它们里面都有一个for循环,它们都要执行让变量count自增的操作

int count = 0;
for(int i = 0;i<10000;i++){
    count++;
}

按照我们的预期,count最终的结果不出意外的话应该20000,但是最终的结果总是在10000到20000之间。出现这种情况的原因就是因为count++这个动作它不是原子性的。

原子性就表明这个动作不能再细分,而count++这个动作就可以分为1.从内存中取出count数据,2.让count加一,3.把count放回到内存中。

不是原子性的操作在多线程编程中就有可能出问题,比如同一时刻线程A和线程B同时将内存中的count假设为0读入CPU中,然后又同一时间将count加一然后放回到内存中,。count有两次动作本应该为2,但是结果却为1,而从这里开始就出问题了。

上面这个列子就可以说明线程不安全所有原因。

上面已经分析了线程的操作不是原子的,再来分析其他两个。

引起线程不安全的根本原因就是因为多线程之间的并发执行,这个是无法从根本上上改变的。并发执行就导致同一时间段内多个线程之间抢占式的执行,就如上面的操作一样,线程A和线程B之间是不知道对方存在的,它们才没有什么礼让,谁先到就先执行。因为执行的顺序都是随机的,因此每次得到的结果肯定也不一样,最极端要么结果为10000要么为20000,结果为10000的情况那就是每时每刻两个线程的动作都是一样的,而20000的情况则两个线程是顺序执行,线程A先执行,线程B后执行。

两个线程操作同一对象就跟不用说了,count作为被操作的对象它是没有意识的,他不能决定这一时刻让A执行,下一时刻让B执行。如果两个线程操作的是不同的对象,比如让线程A操作count,线程B同样的逻辑操作另一个变量ret,则结果都是10000。

通过一个例子来看内存可见性问题:

现在有两个线程A、B,线程A中有一个while循环,判断条件为flag==0。线程B里面执行让用户输入整形数字falg的逻辑。

public class Thread18 {
    static class  Counter{
        private static int flag = 0;
    }
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread thread = new Thread(() -> {
            while(counter.flag==0){

            }
            System.out.println("线程结束");
        });
        thread.start();

        Thread thread1 = new Thread(() -> {
            Scanner scan = new Scanner(System.in);
            System.out.println("请输入一个整数:");
            counter.flag = scan.nextInt();
        });
        thread1.start();
    }
}

结果并没有输出“线程结束”。这就是多线程中内存可见性带来的线程不安全问题。

首先明确是什么可见性,可见性指的是一个线程对内存中的一个共享变量进行修改操作,这个修改操作对其他线程是可见的,例如一个读一个写,但是当写操作的线程进行的修改的时候,读线程可能读到修改前的数据。

出现这样的原因主要和底层编译器的优化相关,在Java内存模型中每个线程都有自己的工作内存(CPU缓存),同时这些线程还有一个共享变量的主内存。当线程要读取一个共享变量时,会先把变量从主内存中拷贝到工作内存,再从工作内存读取数据。当线程要修改一个共享变量的时候,也会先修改工作内存中的副本,然后将再将副本放回到主内存中。

上面的这些情况,对于单核CPU,同一时刻只能有一个线程在执行,所以从主内存中获取到共享变量后,对其进行修改后存放在CPU的缓存中,然后再刷回主内存中, 这整个过程只有一个线程操作,因为为每个线程分配了时间片,所以看起来是多个线程同时执行,但实际上只有一个线程执行。

再回到上面的那个问题,没有输出“线程结束”的原因是thread线程拿取数据时,它是先在自己的工作内存中拿,他自己工作内存中保存的flag就是0,而由于thread1修改的数据没有及时更新到主内存中 ,所以就导致thread线程获取的flag没有改变,所以就不会输出"线程结束"。

指令重排序指的是编译器为了提高程序的执行效率,而改变代码的执行顺序,这样做在多线程下运行就可能出错。举个例子,比如现在要求你按着顺序来做一套试卷,而经历题海战术的你知道什么题简单,因此你会先做简单的题目,最后再做困难的,这就是类似指令重排序。

指令重排序在多线程中可能会出现问题:

clsaa test{
    private static Integer value = 1;
    boolean flag;
    public static void init(){
        this.value = 8;
        this.flag = true;
    }
    public static void getValue(){
        if(flag){
            System.out.println(value);
        }
    }
}

在单线程中输出的value值为8,但是在多线程中输出的值就可能不为8了,原因如下:

假设现在有两条线程Thread1和Thread2,Thread1执行该程序的时候,可能是这样执行的this.flag = true。而在Thread1执行之后,Thread2恰好执行getValue方法,线程2输出的value值就为0。

本来Thread1应该先执行this.value = 8的,但是就是因为指令重排序让它先执行this.flag = true,于是就出现了问题。

为了解决多线程问题,常用的就是给线程加锁或者是在变量前面加上volatile或者是使用线程安全类、线程安全的集合等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/482834.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vscode搭建ESP-IDF开发环境指南-Ubuntu ESP32-C3 合宙

文章目录 1.Ubuntu环境搭建2.vscode安装3.esp-idf插件如果到这里没出现问题的话&#xff0c;就能顺利安装了&#xff0c;如果出现问题会在输出那一栏里报错&#xff0c;根据错误去找原因就好&#xff0c;常见的错误就是网络原因以及之前的依赖包没有装好 ![](https://img-blog.…

优思学院|受控文件在质量管理体系中的作用?

在质量管理体系中&#xff0c;受控文件是指受到控制和管理的文件&#xff0c;包括政策、程序、指南、规程、说明书、作业指导书、记录等&#xff0c;它们记录了组织内各种活动的要求和实施方法&#xff0c;并规定了文件的创建、审批、发布、变更和废止等流程&#xff0c;以确保…

宝塔怎么安装青龙面板-跑京东豆脚本

一、搭建青龙面板 安装docker管理器 我们使用宝塔的Docker管理器来一键安装Docker 在软件商店内搜索Docker,直接安装第一个应用即可。 拉取镜像运行容器 在安装完docker之后我们就可以开始拉取docker镜像并运行容器了,ssh连接服务器命令行中输入下面的代码: docker run …

TOGAF架构内容—TOGAF 内容框架和企业元模型

一、概述 TOGAF ADM 提供了一个流程生命周期&#xff0c;用于在企业内创建和管理架构。在 ADM&#xff0c;对输入、输出和步骤的讨论&#xff0c;描述了许多架构工作产品或工件&#xff0c;例如过程和 应用。 此处提供的内容框架和企业元模型为这些术语定义了正式结构&#x…

【python】NameError: No such file or directory 问题解决

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 1. 问题 最近有小伙伴经常问到这个报错&#xff0c;今天来分享一下具体怎么解决。 [Errno 2] No such file or directory: ./mnist_image_label/mnist_train_jpg_60000.txt这个没有查找到子文件或者子文件夹的问题 2. 解决…

WIZnet 的 TOE 设计大赛

链接: TOE Design Contest 介绍 欢迎来到 WIZnet 的年度物联网设计大赛&#xff01; TOE 竞赛是对您的 IoT 技能和创造力的终极考验。 借助强大的 W5300 网络控制器芯片&#xff0c;您将能够以前所未有的方式将您的物联网愿景变为现实。 无论您是经验丰富的专业人士还是刚刚起…

计算机提示vcruntime140.dll丢失是什么意思?vcruntime140.dll丢失的解决方法(详细方法)

计算机丢失vcruntime140.dll是什么意思&#xff1f;经常看到有小伙伴有在网上问这样的问题&#xff0c;电脑上这个vcruntime140.dll文件丢失的问题经常发生吧&#xff0c;那么就很有必要给大家详细的说说这一方面的问题了&#xff0c;下面我们来看看 第一&#xff1a;vcruntim…

RocketMQ的简单使用

大家好&#xff0c;我是Leo&#xff01;今天来和大家分享RocketMQ的一些用法。 领域模型介绍 Producer: 用于生产消息的运行实体。 Topic: 主题&#xff0c;用于消息传输和存储的分组容器。 MessageQueue: 消息传输和存储的实际单元容器。 Message: 消息传输的最小单元。 C…

如何让你的 WebSocket 接口测试更高效?拯救你的接口测试工作

目录 引言 WebSocket介绍 HTTP与WebSocket的区别 WebSocket测试方法 使用在线工具 使用Postman 使用Jmeter 使用Python 结语 引言 你是否曾经为 WebSocket 接口测试中复杂的协议和难以捕获的数据而感到束手无策&#xff1f;WebSocket 协议与传统的 HTTP 协议不同&…

【牛客网】美国节日与因式分解

目录 一、编程题 1.美国节日 2.因式分解 一、编程题 1.美国节日 链接&#xff1a;美国节日__牛客网 (nowcoder.com) 和中国的节日不同&#xff0c;美国的节假日通常是选择某个月的第几个星期几这种形式&#xff0c;因此每一年的放假日期都不相同。具体规则如下&#xff1a…

volatile与synchronized

文章目录 前言一、简介volatilesynchronized 二、名词解释可见性原子性指令重排临界区对象锁类锁 二、实战使用1 Volatile可以解决的问题2 volatile无法解决非原子性操作问题--synchronized 总结 前言 volatile与synchronized 都是java的关键字 volatile一般修饰变量,被修饰的…

做了一年csgo搬砖项目,还清所有债务:会赚钱的人都在做这件事 !

前段時间&#xff0c;在网上看到一句话&#xff1a;有什么事情&#xff0c;比窮更可怕&#xff1f; 有人回答说&#xff1a;“又忙又窮。” 很扎心&#xff0c;却是绝大多数人的真实写照。 每天拼死拼活的996&#xff0c;你有算过你的時间值多少钱&#xff1f; 我们来算一笔…

操作系统——死锁

0.关注博主有更多知识 操作系统入门知识合集 目录 5.1死锁概念 5.2死锁的起因 5.3预防死锁的策略 思考题&#xff1a; 5.1死锁概念 在介绍死锁之前&#xff0c;先来探究一个问题&#xff1a;哲学家就餐问题。五个哲学家围坐在圆桌边&#xff0c;有5支筷子&#xff0c;哲…

免费的绘图工具DrowIO下载及安装

还在为论文绘图而烦恼吗&#xff1f;还在为如何选择画图工具而烦恼吗&#xff1f;没事&#xff0c;本期就给你们推荐一款超级好用且免费的绘图工具——DrawIO。 目前使用比较多的绘图工具有&#xff1a;Visio、亿图图示、Word、PPT、DrawIO等 其中DrawIO由于其既实用又免费的…

使用 Esp32 和 TinyML 进行手势分类

介绍 手势分类是机器学习可以做什么的一个简单但同时又很好的例子。它使用大量“混乱”的数据来对事物进行分类。 在这个项目中,我们将制作一个包含 4 个类的分类器,idle、up_down、left_right 和 circle。 数据采集 要将数据上传到 Edge Impulse,我们需要使用 Edge Imp…

199. 二叉树的右视图【111】

难度等级&#xff1a;中等 上一篇算法&#xff1a; 236. 二叉树的最近公共祖先【190】 力扣此题地址&#xff1a; 199. 二叉树的右视图 - 力扣&#xff08;Leetcode&#xff09; 1.题目&#xff1a;199. 二叉树的右视图 给定一个二叉树的 根节点 root&#xff0c;想象自己站在…

JavaScript事件

事件流描述的是从页面接收事件的顺序。比如说单击了某个按钮&#xff0c;但是单击事件不仅发生在按钮上&#xff0c;在单击按钮的同时&#xff0c;也单击了按钮的容器元素&#xff0c;甚至是 <body> 、<html> 、document。 事件传播的顺序不同导致存在两种事件流机…

初识CPU(二)

目录 一、控制器的功能与工作原理 1.控制器的设计思路 2.控制器的分类 3.微程序 3.1微命令 3.2微操作 3.3微指令 3.4微程序 3.5微地址 4.控制方式 4.1同步控制方式 4.2异步控制方式 4.3联合控制方式 4.4人工控制方式 二、微指令 5.微指令的编码方式 5.1直接编码…

基于springboot的家政服务管理平台(源码,设计文档等)

摘要 随着家政服务行业的不断发展&#xff0c;家政服务在现实生活中的使用和普及&#xff0c;家政服务行业成为近年内出现的一个新行业&#xff0c;并且能够成为大众广为认可和接受的行为和选择。设计家政服务管理平台的目的就是借助计算机让复杂的销售操作变简单&#xff0c;…

kafka的安装与使用

文章目录 kafka安装1 上传安装包2 解压安装包3 创建logs文件夹4 修改配置文件5 分发kafka6 启动kafka kafka使用1 启动kafka2 关闭kafka3 查看topic4 创建topic,名称为test5 删除名称为test的topic6 向topic发送数据7 从topic里消费数据 kafka安装 kafka安装前需要确认zookeep…