JavaEE之线程(4)——线程安全、线程安全的原因,synchronized关键字

前言

在本栏的前面的内容中,我们介绍了线程的创建、Thread 类及常见方法、线程的状态,今天我们来介绍一下关于线程的另一个重点知识——线程安全。

一、线程安全

基本概念:

线程安全的确切定义是复杂的,但我们可以这样认为:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

我们给出一个具体例子,这个自加到10000的例子将会很好的体现线程安全:

static class Counter {public int count = 0;void increase() {count++;}
}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);-----------------------------------------------输出结果:59970
}

那么问题来了,理想输出结果为100000,但实际结果为什么不符合呢?

二、线程不安全的原因

首先,让我们理解一下什么是原子性

2.1 什么是原子性

 我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?

是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进 不来了。这样就保证了这段代码的原子性了

一条 java 语句不一定是原子的,也不一定只是一条指令
比如刚才我们看到的 n++,其实是由三步操作组成的:

  1. 从内存把数据读到 CPU
  2. 进行数据更新
  3. 把数据写回到 CPU
  4. 不保证原子性会给多线程带来什么问题
    如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是
    错误的。这点也和线程的抢占式调度密切相关。如果线程不是 “抢占” 的,就算没有原子性,也问题不大。

2.2 上述代码错误的具体原因

count++ 这个操作,站在CPU的角度上,count++是由CPU通过三个指令完成的。

  1. load 把数据从内存, 读到 cpu 寄存器中;
  2. add把寄存器中的数据进行 +1;
  3. save 把寄存器中的数据,保存到内存中。

 ;如果是多个线程执行上述代码,由于线程之间的调度顺序是“随机”的,就会导致在有些调度顺序下,上述的逻辑就会出现问题。比如,我们简单选取4种的情况举下例子:
在这里插入图片描述

上图中1、2两种情况没有发生逻辑错误,因此不会发生错误,但是第三、四情况就会调度顺序随机,造成代码结果出现错误。

2.3 线程安全的具体原因

  1. 操作系统对于线程的调度是随机的;
  2. 多个线程同时修改同一个变量
  3. 修改操作不是原子的;
  4. 内存可见性;
  5. 指令重排序。

三、Synchronized关键字

上述出现的线程不安全问题,通过加锁,就能解决上述问题,其中最常用的办法,就是使用synchronized 关键字。

synchronized 在使用的时候,要搭配一个 代码块{ } ;
在已经加锁的状态中,另一个线程尝试同样加这个锁,就会产生“锁冲突/锁竞争”,后一个线程就会阻塞等待-直等到前一个线程解锁为止。

让我们再次回到上面的那个例子,通过加锁便可以加锁线程安全的问题:
实例代码1:

/*** @author Zhang* @date 2024/5/515:43* @Description:*/
class Counter{public int count;// 1)直接修饰普通方法;synchronized public void increase(){count++;}//上面的写法是下面的简化版本public void  increase2(){synchronized(this){count++;}}// 2)修饰静态方法;synchronized public static void increase4(){}public static void  increase5(){synchronized(Counter.class){}}
}
public class Test2 { public static void main(String[] args) throws InterruptedException {Counter counter = new Counter();Thread  t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {counter.increase();}});Thread t2 = new Thread(()->{for (int j = 0; j < 50000; j++) {counter.increase();}});t1.start();;t2.start();t1.join();t2.join();System.out.println("count:"+counter.count);}
}
------------------------------------------------------输出:100000

实例代码2:

public class Test1 {    private static int count = 0;public static void main(String[] args) throws InterruptedException {Object lock= new Object();Thread t = new Thread(()->{for (int i = 0; i < 50000; i++) {synchronized (lock){count++;}}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {synchronized (lock){count++;}}});//t、t2同时执行t.start();t2.start();t.join();t2.join();//预期结果100000System.out.println("count: "+count);}
}--------------------------------------------------------
输出:100000

总结

好啦!今天我们讲解了线程安全问题,以及为什么会出现线程安全、如何解决线程安全、synchronized关键字。在本栏(https://blog.csdn.net/2301_80653026/category_12660552.html?spm=1001.2014.3001.5482)的下一节我们将继续介绍线程。

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

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

相关文章

vue3 antd-vue 超简单方式实现a-table跨页勾选

效果如下&#xff1a; 一、第一种方法 定义一个变量 selectKeysBack 用于维护所有勾选的数据value, 然后用 onSelect 拿到当前操作的数据&#xff0c;以及时候勾选。 这个方法便于拿到后端的数据之后用于回显 1、template <a-tableclass"custom-table-data":da…

类与对象(二)

封装 封装作为面向对象三大特性&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;之一&#xff0c;那如何实现封装性的呢&#xff1f;就又得拿出上面的访问修饰限定符的图 public: 就是在任何地方都可以访问 protected: 涉及子类在介绍继承时详细介绍 default: …

STK12 RPO模块学习 (1)

一、背景介绍 在STK12中&#xff0c;在Astrogator的模块上开发了新的模块&#xff08;Rendezvous and proximity operations)。轨道交会接近通常来说是一个很复杂的过程。RPO实现需要对轨道动力学有一个清晰的理解&#xff0c;并且对于Astrogator模块具备很强的背景和经验&…

【教学类-55-02】20240512图层顺序挑战(四格长条纸加黑色边框、4*4、7张 、43200张去掉非7色有23040张,去掉重复样式有几种?)

作品展示 背景需求&#xff1a; 之前的代码吗存在几个问题&#xff0c;最大的问题是不能生成“”长条黑边框”” 【教学类-55-01】20240511图层顺序挑战&#xff08;四格长条纸&#xff09;&#xff08;4*4&#xff09;和“手工纸自制参考图”-CSDN博客文章浏览阅读485次&…

第十篇:数字堡垒:操作系统安全深度解析与实战指南

数字堡垒&#xff1a;操作系统安全深度解析与实战指南 1 *引言 1.1 数字世界的守护者 在遥远的比特海中&#xff0c;有一座名为“操作系统”的数字堡垒&#xff0c;它守护着我们的数据宝藏&#xff0c;确保每一次计算的航行都能安全抵达彼岸。然而&#xff0c;这片海域并非风…

记录:robot_localization传感器数据融合学习

一、参考资料 官方&#xff1a; http://wiki.ros.org/robot_localizationhttp://docs.ros.org/en/noetic/api/robot_localization/html/index.html2015 ROSCon 演讲官方网址&#xff08;youyube上也有这个视频&#xff09; 实践教程 https://kapernikov.com/the-ros-robot_…

深入理解Python的类,实例和type函数

问题起源&#xff1a; class t():pass s1 t() s2 type("Student2",(),{}) isinstance(s1, type), isinstance(s2, type)为什么第一个是false&#xff0c;第二个是true呢 根因定位&#xff1a; 在Python中&#xff0c;一切皆对象&#xff0c;类是对象&#xff0c…

Linux字符设备驱动设计

Linux字符设备驱动设计 概述 驱动的定义与功能 计算机系统中存在着大量的设备&#xff0c; 操作系统要求能够控制和管理这些硬件&#xff0c; 而驱动就是帮助操作系统完成这个任务。 驱动相当于硬件的接口&#xff0c; 它直接操作、 控制着我们的硬件&#xff0c; 操作系统通…

升级Microsoft 365后,SAP GUI中无法打开Excel的解决方案

最近&#xff0c;我们遇到了一个棘手的问题&#xff0c;一位客户在升级到Microsoft 365后&#xff0c;无法在SAP GUI中打开Excel。这个问题不仅影响了工作效率&#xff0c;也给用户的日常操作带来了不便。在本文中&#xff0c;我们将探讨问题的成因&#xff0c;并提供一种解决方…

gin自定义验证器+中文翻译

gin自定义验证器中文翻译 1、说明2、global.go3、validator.go4、eg&#xff1a;main.go5、调用接口测试 1、说明 gin官网自定义验证器给的例子相对比较简单&#xff0c;主要是语法级别&#xff0c;便于入门学习&#xff0c;并且没有给出翻译相关的处理&#xff0c;因此在这里记…

PaddleOCR_PP-Structure

静态IP设置 # 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33# 修改文件内容 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic IPADDR192.168.15.132 NETMASK255.255.255.0 GATEWAY192.168.15.2 DEFROUTEyes IPV4_FAILURE_FATALno IPV6INIT…

YOLOv8小白中的小白安装环境教程!没一个字废话,看一遍不踩坑!

文章目录 去哪里下代码?怎么下代码?怎么装环境?命令行界面(CLI)指令和Python脚本区别?附录1 conda常用指令附录2 git常用指令附录3 项目代码文件作用去哪里下代码? 下载代码请大家直接去 YOLOv8的官方仓库下载,名字叫 ultralytics,有些镜像网站和个人发的等来历不明的代…

CMakeLists.txt语法规则:foreach 循环基本用法

一. 简介 cmake 中除了 if 条件判断之外&#xff0c;还支持循环语句&#xff0c;包括 foreach()循环、while()循环。 本文学习 CMakeLists.txt语法中的循环语句。 CMakeLists.txt语法中 有两种 循环实现方式&#xff1a;foreach循环与 while循环。 二. CMakeLists.txt语法规则…

解决 SyntaxError: Unexpected token ‘.‘ 报错问题

这个报错一般是编译问题&#xff0c;浏览器的版本过低没通过代码 解决办法&#xff1a; 在package.json文件中加上这个 "browserslist": ["> 1%","last 2 versions","not dead","not ie < 6","Android > 4&…

AI算法工程师课程学习-数学基础-高数1-微积分

基础数学学习目的&#xff1a;为机器学习(尤其是算法)和深度学习打基础。 学习路线&#xff1a;1.高中数学-->大学2.微积分-->3.线性代数-->4.概率论-->5.优化理论。&#xff08;因为自己40多岁年龄了&#xff0c;做了7年的.net开发&#xff0c;后面都一直在做软件…

工器具管理(基于若依)

文章目录 前言一、工器具管理项目总览 二、入库功能1. 前端1.1 界面展示1.2 具体操作实现1.3 js文件 2. 后端2.1 工器具信息回显2.2 工器具入库 三、领用功能1. 前端1.1 界面展示1.2 具体实现操作1.3 js文件 2. 后端2.1 工器具信息回显2.2 工器具领用 遇到的问题1. 同一页面展示…

2024 年中国大学生程序设计竞赛全国邀请赛(郑州)暨第六届CCPC河南省大学生程序 设计竞赛Problem L. Toxel 与 PCPC II

//sort bug下标 遍历dp. //没修负的bug肯定连续 #include<bits/stdc.h> using namespace std; #define int long long const int n1e611; int a,b,c[n],dp[n]; signed main() {ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>a>>b;for(int i1;…

【JVM】从可达性分析,到JVM垃圾回收算法,再到垃圾收集器

《深入理解Java虚拟机》[1]中&#xff0c;有下面这么一段话&#xff1a; 在JVM的各个区域中&#xff0c;如虚拟机栈中&#xff0c;栈帧随着方法的进入和退出而有条不紊的执行者出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的&#xff08;尽管在…

抖音上有可以长久赚钱的副业吗?当然有,只有它最稳定长久!

大家好&#xff0c;我是电商糖果 现在有很多年轻人在大城市上班&#xff0c;发现辛辛苦苦一年也赚不到多少钱。 如果说自己有了房贷&#xff0c;车贷&#xff0c;那更是一点儿不敢歇。 为了可以有更多的收入&#xff0c;年轻人都希望可以靠着下班时间&#xff0c;找一个可以…

[前后端基础]图片详解

[前后端基础]图片传输与异步-CSDN博客 https://juejin.cn/post/6844903782959022093#heading-3 base64、file和blob用JS进行互转的方法大全【前端】_js base64转blob-CSDN博客 后端存储方式 对于第一种存储方式&#xff0c;我们前端直接将存储路径赋值给 src 属性即可轻松显示。…