多线程04 线程安全问题以及一些简单的解决策略

前言

首先我们引入多线程是为了解决多次创建进程和销毁进程带来的巨大开销,线程可以共享内存和硬盘资源等等,这里我们就会想,他们共享这些东西会不会涉及到一些安全问题呢?他们没有独立分配自己的资源是一定会有安全问题的,但是就目前在这个快节奏的社会来说,效率的提升是必然需要的,我们只能去发现和解决这些安全问题,效率第一!!

举例

下面我们给出一段代码,我们从代码的效果和原因来解析这段代码产生的线程安全问题以及解决方案.

package Thread;public class ThreadDemo17 {private static int count = 0;private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {count++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}

我们这里创建两个线程,t1和t2让他们合作完成100000次自增操作,并且阻塞main线程,最后让main线程来收集结果.

下面我给出几次运行产生的结果

运行相同的代码,怎么就一直产生错误的结果,而且错误的还是不一样的呢???

.

下面我给出解析举例

我们观察一下count++这一个自增操作需要几步吧

这里为我们其实只有三步

第一步是取元素,可以看到是getstatic取到count这个变量

第二步是自增1,就是iadd这个操作

第三步是putstatic,气死就是一个保存的操作

iconst_1其实是将1放到栈区的操作数栈顶

由于这个count++的操作是不原子的,所以这里会产生线程安全的问题

下面我们模拟一下不安全的一种用例(以下操作将上述多个操作抽象成取值,自增,保存三个操作)

假设这里只有一次自增,t1中的count先加载为0,此时t2读到的count也是0,t1最后自增保存了一个1,t2进行更新的时候其实更新的也是1,这就会造成了线程不安全问题,最后导致的结果是1而不是2,与我们的理想情况相悖.

导致这个结果的原因有很多,最重要的就是cpu的这种抢占式调度系统,你无法控制cpu先调度哪个线程,执行到哪个命令之后执行其他的命令.

解决方案 

这里我们采用锁来保证线程的安全性,你可以理解为此时张三正在上厕所,咔嚓以下把门锁起来了,这个时候,你再急也进不去,完成不了你的任务,即使此时线程调度到你了,你也只是一个阻塞的状态,无法完成任务

我们使用synchronized关键字来修饰,我们会发现需要一个参数,这个参数其实无论你造一个什么对象都是可以的,只要保证这两个线程的参数是同一个对象即可

举例如下

package Test;public class ThreadDemo1125 {private static int count = 0;private static final Object lock = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 10000; i++) {synchronized (lock){count++;}}});Thread t2 = new Thread(()->{for (int i = 0; i < 10000; i++) {synchronized (lock){count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = "+ count);}}

此时线程就是安全的了,但也意味着线程由完全并发式的执行变成了半并发半串行的执行

我们发现代码中,synchronized修饰的代码块是串行执行的,但是其他代码还是并发执行的,所以相较于完全串行使用一个线程来执行还是提升了不少的效率的,下面我们来看代码运行结果

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

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

相关文章

Python编程之魂之运算符的优先级教程

文章目录 前言优先级概述相同优先级结合性运算符优先级一览表运算符优先级重点说明结语关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资…

深度学习基于Python+TensorFlow+Django的水果识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介技术组合系统功能使用流程 二、功能三、系统四. 总结 一项目简介 # 深度学习基于PythonTensorFlowDjango的水果识别系统介绍 简介 该水果识别系统基于…

PaddleOCR学习笔记

Paddle 功能特性 PP-OCR系列模型列表 https://github.com/PaddlePaddle/PaddleOCR#%EF%B8%8F-pp-ocr%E7%B3%BB%E5%88%97%E6%A8%A1%E5%9E%8B%E5%88%97%E8%A1%A8%E6%9B%B4%E6%96%B0%E4%B8%AD PP-OCR系列模型列表&#xff08;V4&#xff0c;2023年8月1日更新&#xff09; 配置文…

【论文解读】Edit-DiffNeRF:使用2D-扩散模型编辑3D-NeRF

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2306.09551 摘要 最近的研究表明&#xff0c;将预训练的扩散模型与神经辐射场&#xff08;NeRF&#xff09;相结合&#xff0c;是一种很有前途的文本到 3D 的生成…

使用bard分析视频内容

11月21日的bard update 更新了分析视频的功能&#xff0c;使用方法如下&#xff1a; 1、打开bard网站。https://bard.google.com/ 2、点击插件。 3、点击YouTube中的 research a topic 选项。 4、输入需要分析的内容&#xff1a; Please analyze how many technologies are in…

企业计算机中了locked勒索病毒怎么解锁,locked勒索病毒解密,数据恢复

科技的进步为企业的生产生活提供了极大便利&#xff0c;但随之而来的网络安全威胁也不断增加&#xff0c;近期云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的计算机服务器遭到了locked勒索病毒攻击&#xff0c;导致企业的所有业务无法正常开展&#xff0c;所有计算…

接口测试场景:怎么实现登录之后,需要进行昵称修改?

在接口测试中有一个这样的场景&#xff1a;登录之后&#xff0c;需要进行昵称修改&#xff0c;怎么实现&#xff1f; 首先我们分别看下登录、昵称修改的接口说明&#xff1a; 以上业务中补充一点&#xff0c;昵称修改&#xff0c;还需要添加请求头Authorization传登录获取的to…

LeetCode Hot100 114.二叉树展开为链表

题目&#xff1a; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同…

【docker】docker的基础命令

基础操作 docker info #查看docker的基本信息docker version #查看docker版本信息一、镜像操作 1、搜索镜像 docker search nginx2、下载镜像 docker pull nginx#从仓库中下载镜像&#xff0c;若没有指定标签&#xff0c;则下载最新的版本&#xff0c;也就是标签为: lat…

【C++初阶】STL详解(六)Stack与Queue的介绍与使用

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

C/C++ 通过SQLiteSDK增删改查

SQLite&#xff0c;作为一款嵌入式关系型数据库管理系统&#xff0c;一直以其轻量级、零配置以及跨平台等特性而备受青睐。不同于传统的数据库系统&#xff0c;SQLite是一个库&#xff0c;直接与应用程序一同编译和链接&#xff0c;无需单独的数据库服务器进程&#xff0c;实现…

力扣114. 二叉树展开为链表(java,用树模拟链表)

Problem: 114. 二叉树展开为链表 文章目录 题目描述思路解题方法复杂度Code 题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 1.展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左…

leetcode刷题详解二

160. 相交链表 本质上是走过自己的路&#xff0c;再走过对方的路&#xff0c;这是求两个链表相交的方法 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {//本质上是走过自己的路&#xff0c;再走过对方的路if(headA NULL|| headB NULL){return NULL;}Lis…

el-uploader同一文件无法上传问题

在上传成功和失败的回调方法中&#xff0c;吊用一下clearFiles方法。 this.$refs.upload.clearFiles();

7、独立按键控制LED状态

按键的抖动 对于机械开关&#xff0c;当机械触点断开、闭合时&#xff0c;由于机械触点的弹性作用&#xff0c;一个开关在闭合时不回马上稳定地接通&#xff0c;在断开时也不会一下子断开&#xff0c;所以在开关闭合及断开的瞬间会伴随一连串的抖动 #include <REGX52.H…

我叫:希尔排序【JAVA】

1.我兄弟存在的问题 2.毛遂自荐 希尔排序提希尔(Donald Shell)于1959年提出的一种排序算法。 希尔排序&#xff0c;也称递减增量排序算法&#xff0c;是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。 希尔排序是基于插入排序的以下两点性质而提出改进方法的&…

postman和Jmeter做接口测试的区别(经验之谈)

接口测试的目的 API 测试作为集成测试的一部分&#xff0c;经过被测应用的接口&#xff08;API&#xff09;来确定是否在功能、可靠性、性能和安全方面达到预期的软件测试。因为 API 都没有 GUI 界面&#xff0c;API 测试都是在通信层进行的。 1.建立接口用例集 Postman功能…

第十八章 解读pytorch优化器与学习率设置(工具)

简介与解读基本概念 学习率对于模型训练效果来说相当重要。 学习率过低会导致学习速度太慢&#xff0c;学习率过高又容易导致难以收敛。 因此&#xff0c;很多炼丹师都会采用动态调整学习率的方法。刚开始训练时&#xff0c;学习率大一点&#xff0c;以加快学习速度&#xf…

2016年11月10日 Go生态洞察:七年的Go语言旅程

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

面试必问:如何快速定位BUG?BUG定位技巧及N板斧!

01 定位问题的重要性 很多测试人员可能会说&#xff0c;我的职责就是找到bug&#xff0c;至于找原因并修复&#xff0c;那是开发的事情&#xff0c;关我什么事&#xff1f; 好&#xff0c;我的回答是&#xff0c;如果您只想做一个测试人员最基本最本分的事情&#xff0c;那么可…