【JAVA ee初阶】多线程(3)

一、出现线程安全的原因

1.【根本原因】线程的调度执行时随机的(抢占式执行)->罪魁祸首

2.多个线程同时修改同一个变量

如果是一个线程修改一个变量 或者 多个线程读取同一个变量 或者 多个线程修改不同变量 这些都没事。

3.修改操作不是原子的!

像count++ 这样的修改,就是不是原子的修改。

但是像 = 这样的修改,就是属于原子的。(在Java针对内置类型进行 = ,或者针对引用 = 都是原子的)(*但是,在C++中就不一定了,还是不是原子的,就得具体问题具体分析了)

后序判定某个代码是否是线程安全的,需要结合这几点一起来判定。

*如果将全局变量count放入到main方法当中,就会出现报错。

把变量改成局部变量,编译直接过不了了:lambda表达式要想正确捕获变量,要求是final或者事实final(虽然没有加final关键字,但是代码中确实也没人修改)

写成成员变量在lambda中确实能够使用,此时不是走“变量捕获”语法,而是“内部类访问外部类成员”,本身就ok。

lambda本质上就是匿名 内部类(函数式接口)

内部类访问外部类成员,本来就可以实现。

*扩展String 

String是不可变对象,new好一个String对象本身就是不能修改的。设计成不可变对象,其中有一个理由,就是不可变对象天然就是线程安全的。

不可变对象,方便放到常量池中进行缓存。不可变对象,hasCode是固定值,也方便和哈希表进行结合,作为hash的Key

StringBuffer本身确实修改了,但是又通过其他路径(例如枷锁)解决线程安全问题。

StringBuilder是彻底的线程不安全。

*什么是原子的?一个事物是原子的,说明他就是不可拆分的最小单位。SQL中,事务就是把多个SQL打包成一个整体,执行的时候,要么全都执行完,要么一个都不执行。就不会出现执行一半的情况,这就成为原子性。

此处谈到的原子也是类似的含义,CPU执行指令的角度,如果是一条指令,对于CPU来说,要么就是执行完,要么就是不执行,不会出现“一个指令执行一般”这样的情况。CPU执行一条指令,这个行为就是原子的。

像count++这样的指令,对应到多条CPU指令,CPU执行过程中就可能执行一半,就调度走执行别人的指令了(这就不是原子的)

=这样的操作,也是对应到一条CPU指令(类似于MOVE)

那么如何将这些操作变为线程安全的呢?

核心思路:把修改操作变成原子的。

通过锁来实现。

关键字:synchronized通过这个关键字来实现使用锁。

对于锁这样的概念,涉及到两个核心操作:

1.加锁

2.解锁

Java就通过这一个关键字来表示这两种操作,进来就是加锁,出去就是解锁。sychronized()的()里面填写的是“锁对象”,真正用来枷锁的锁是谁?——>在Java中,任何一个对象都可以用来作为锁对象(引用类型,不能是内置类型)

加锁,就是把若干个操作“打包成一个原子”。不是说把这count++的三个指令变成一个指令了。也不是说,这三个指令就必须要一口气在cpu上执行完,不会触发调度。加锁会影响到其他的加锁线程,而且是加同一个锁的线程。

当两个线程,尝试竞争同一把锁,才会产生“阻塞”,如果是竞争不同的锁,就没有影响。

sychronized(锁对象),看锁对象是不是同一个对象。

锁竞争(Lock Contention)是指多个线程试图同时访问同一个临界区(即需要互斥访问的代码区域),因此它们之间产生了竞争。在任意时刻,只能有一个线程持有锁并进入临界区,其他试图进入临界区的线程必须等待。

如果是两个线程,一个加锁了另一个么有加锁,这样就不会产生阻塞。两个线程都加锁了,而且是同一个对象,才会产生锁竞争。

此处的加锁和解锁,也可以视为两个cpu指令。这两个操作使得这两个线程各自循环执行5w次。

整个程序按照如图所示的流程进行:

本来load add save 在两个线程中是穿插执行的,但是在引入锁之后,就变成了“串行执行”,不再穿插,最后输出结果也自然是1w次。

*要是两个锁对象不一样:不一样就不会产生阻塞,程序的执行也就不会出现上述的“禁止插队”这样的行为。

这里又一系列很复杂的逻辑这里也有一系列很复杂的逻辑

日常工作中,一般都是让加锁范围尽量的小

这样的话,可以并发执行的逻辑就更多,此时外部的逻辑通常是更复杂的。

此时这张图中,只有count++是串行的。

引入多线程,就是为了并发执行,就是为了充分利用cpu多核心资源。

多进程编程 和 多线程编程 就是在利用多核心的编程手法。

t1如果加上锁,t1就会不停地执行循环,直到把5w次都执行完,才会去释放锁。

t2只能阻塞等待,一直等到t1释放锁(t1的5w次循环都执行完了),t2才能继续执行

此时,这两个线程的循环时完全串行的,(也就是和一个线程执行是类似的了),这种写法,两个代码是完全串行化。

此时,并没有把多核心利用起来。

这种写法,在当前代码下,执行速度反而更快,主要是因为当前的任务很简单。

*这种情况下,t1和t2谁先拿到锁?

结论没有唯一性,假设t1先拿到锁,当t1循环完毕一次,下一次加锁可能是t1继续加上,也可能是t2加上(这里体现了随机调度)。类似于“数据库隔离级别”,隔离级别越高,并发程度越低,执行速度越慢。

synchronized使用方法

1.synchronized(锁对象){}

基础使用,常见使用。不是禁止调度,而是禁止其他线程插队。

2.修饰一个普通方法,就可以省略锁对象:

*此时,相当于针对this加锁,不是没有锁对象,而是把锁对象给省略掉了

等价于

上面这两种写法,实际上没有任何区别。锁对象,是啥对象不重要,重要的是,两个线程是否是针对同一个对象加锁。

3.synchronized修饰静态方法

此时认为synchronized是针对类对象进行加锁的

static修饰的方法,也叫做“类方法”,不是针对“实例”的方法,而是针对类的。在这个方法里,没有this。

Counter.class——>反射 程序运行时,能够拿到类/对象的一些相关属性。(这个类有哪些成员,都叫啥名字,都是啥类型,都是private/public,有啥方法,都叫啥名,参数列表是啥,是private/public,这个类继承的父类是谁,上实现了哪些interface.......)

通过类对象拿到上述信息。(*不考虑运行,看下代码就行,强调“运行时”的意思就是,不让你看代码,也能够获取到这里的信息)

.java编译生成.class 

.class被jvm加载到内存中,就得到了对应的“类对象”。

synchronized的特性

1.互斥性(前文已经提及)

2.可重入

如果一个线程,针对一把锁,连续加锁两次会发生死锁(deadlock)。(如图所示)

分析:初始情况下,假定locker是未加锁的状态,此时的synchronized就会加锁成功,继续向下执行。

第二次加锁的时候,这个锁已经是“被加锁”的状态了,如果这个锁已经被加锁了,尝试加锁操作,就会触发锁冲突/锁竞争,此时该线程就会阻塞等待,一直等到锁被释放。

走到第二个加锁的位置,触发阻塞,如何解除阻塞?得先释放第一个锁,如何释放第一个锁,得先把第二个锁加上,往下继续走。

BLOCKED状态不是“死锁”,而是因为锁产生的阻塞,这里所说的死锁指的是这个锁再也解不开了。

有的时候,死锁的现象不是特别明显,稍有不慎就会发生死锁现象。

但是,java中引入了可重入机制,有效地避免了上述的死锁情况。(注意,死锁有很多种体现形式,可重入只是能解决一个线程一把锁,加锁两次的情况,解决不了其他情况)同一个线程,针对同一把锁,连续加锁多次,不会触发死锁,此时这个锁就可以称为“可重入锁”。

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

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

相关文章

Halcon 3D 表面匹配基于形状

文章目录 prepare_object_model_3d 准备 3D 物体模型read_shape_model_3d — 读取3D匹配模型create_shape_model_3d 准备要匹配的3D模型find_shape_model_3d ——发现匹配模型project_shape_model_3d 将三维形状模型的边缘投影到图像坐标中。示例ignore_part_polarity&#xff…

【Linux】Java 开发者的 Linux 常用命令指南

Java 开发者的 Linux 常用命令指南 目录标题 Java 开发者的 Linux 常用命令指南1. Linux 目录结构2. 系统信息命令3. 服务管理系统服务防火墙管理 4. 文本编辑 (vi/vim)常用模式 5. 文件和目录操作查看与导航创建与删除查看文件内容查找文件 6. 用户管理7. 压缩和解压8. 权限管…

每日c/c++题 备战蓝桥杯(P1252洛谷 马拉松接力赛)

洛谷P1060 马拉松接力赛题解:贪心算法在资源分配中的巧妙应用 题目描述 P1060 马拉松接力赛是一道结合贪心策略与动态规划思想的资源分配问题。题目要求将25公里的马拉松接力赛合理分配给5名选手,使得总耗时最短。每位选手可跑1-10公里的整数距离&…

Nginx 中间件

Nginx(发音为 "engine-x")是一款开源的高性能 HTTP 服务器和反向代理服务器,最初由 Igor Sysoev 开发。 它以其高性能、稳定性、丰富的功能集和低资源消耗而闻名,广泛应用于全球的 Web 服务架构中。 作为中间件&#…

Neo4j在win下安装教程(docker环境)

1. 安装命令 1.1 基于正式neo4j安装–不用 docker run --name neo4j-container -p 7474:7474 -p 7687:7687 -d neo4j1.2 基于community安装 需要部署两个Neo4j,一个正式库prod,一个测试库dev。 neo4j默认监听7474(HTTP-也就是浏览器端口&…

kylin v10 + argo + ascend 310p多机多卡 pytorch distributed 训练

最近接了个模型训练编排多机多卡的改造需求,要求使用argo dag task启动多个节点,同时多个节点能实现 torch.distributed.launch 这样多机多卡的训练模式 简述技术 torch.distributed.launch命令介绍 我们在训练分布式时候,会使用到 torch.d…

[Mac] 使用homebrew安装miniconda

使用虚拟环境可以对不同项目的依赖进行隔离。可以使用venv或者conda来创建和使用虚拟环境。 venv是Python内置的虚拟环境管理模块,适合纯Python项目以及快速轻量级的开发和部署。conda具备更强大的版本管理能力,但是占用较大的磁盘空间。 考虑到我基本不…

CMU-15445(1)——环境搭建

前言 最近在找完暑期实习之后,终于有了一些干项目外的空余时间学习新的知识,在这么多轮面试中,数据库的考察非常多,但孱弱的数据库基础导致我有很多次面试被问住,因此我希望在学习CMU-15445(Fall 2024&…

CSS元素动画篇:基于当前位置的变换动画(四)

基于当前位置的变换动画(四) 前言透明效果类元素动画闪烁动画效果效果预览代码实现 淡入动画效果效果预览代码实现 淡出动画效果效果预览代码实现 结语 前言 CSS元素动画一般分为两种:一种是元素基于当前位置的变换动画,通过不明…

STM32驱动AD5318配置8通道DA详细讲解

目录 1. AD5318 芯片特性 2、AD5318寄存器概述 3、SPI数据帧格式 3.1 控制位(Bit15) 3.2 地址位(Bit14-Bit12,3 位) 3.3 数据 / 控制码(Bit11-Bit0) 4、控制功能寄存器(控制位 = 1 时激活) 4.1 参考与增益配置(MM = 00) 4.2. LDAC模式(MM = 01) 4.3 掉…

如何搭建spark yarn 模式的集群集群

以下是搭建Spark YARN模式集群的一般步骤: 准备工作 - 确保集群中各节点安装了Java环境,并配置好 JAVA_HOME 环境变量。 - 各节点间能通过SSH免密登录。 - 安装并配置好Hadoop集群,YARN作为Hadoop的资源管理器,Spark YARN模式需要…

SpringMVC处理请求映射路径和接收参数

目录 springmvc处理请求映射路径 案例:访问 OrderController类的pirntUser方法报错:java.lang.IllegalStateException:映射不明确 核心错误信息 springmvc接收参数 一 ,常见的字符串和数字类型的参数接收方式 1.1 请求路径的…

在 Windows 系统上升级 Node.js

一、查询电脑端已经安装的 Node.js 版本 1、通过【winR】 键,输入 cmd,点击【确定】按钮打开 cmd 窗口 2、命令行界面输入 node -v 查看目前 Node.js 版本 3、命令行界面输入 npm -v 查看目前 npm 版本 二、进入官网地址下载安装包 1、官网地址&#x…

深入详解人工智能数学基础——概率论中的马尔可夫链蒙特卡洛(MCMC)采样

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…

C++ 嵌套类 (详解 一站式讲解)

目录 嵌套类 嵌套类的定义 嵌套类结构的访问权限 pimpl模式(了解) 嵌套类 嵌套类的定义 首先介绍两个概念: 类作用域(Class Scope) 类作用域是指在类定义内部的范围。在这个作用域内定义的成员(包括…

tcp 和http 网络知识

1. 请简述TCP和HTTP的定义与基本概念 TCP:即传输控制协议(Transmission Control Protocol),是一种面向连接的、可靠的、基于字节流的传输层通信协议。它为互联网中的数据通信提供稳定的传输机制,在不可靠的IP层之上&a…

MySQL安装的多个组件中无用组件卸载

在决定卸载MySQL的哪些组件前,需根据你的实际使用场景判断。以下是各组件的主要功能及卸载建议: 1. 核心组件卸载建议 组件名称作用是否可卸载MySQL Server数据库服务核心,存储数据、处理SQL请求的核心程序。不可卸载 (卸载会导致…

CosyVoice 技术全景解析:下一代语音生成模型的革命性突破

目录 一、CosyVoice 模型概述 1. 背景与定位 二、技术架构与创新 1. 核心架构设计 2. 关键技术亮点 三、行业地位与竞品对比 1. 市场定位分析 2. 竞争优势 四、部署方案与硬件成本 1. 硬件需求 2. 优化技巧 五、优势与挑战 1. 核心优势 2. 主要挑战 六、开源生态…

rabbitmq-集群部署

场景:单个pod,部署在主节点,基础版没有插件,进阶版多了一个插件 基础版本: --- apiVersion: v1 kind: PersistentVolume metadata:name: rabbitmq-pv spec:capacity:storage: 5GiaccessModes:- ReadWriteOncestorage…

[密码学实战]商用密码产品密钥体系架构:从服务器密码机到动态口令系统

[密码学实战]商用密码产品密钥体系架构:从服务器密码机到动态口令系统 关键词:商用密码、密钥体系、服务器密码机、金融数据密码机、动态口令、智能密码钥匙 摘要:本文深度解读商用密码产品的核心密钥体系架构,涵盖服务器密码机、金融数据密码机、VPN产品、动态口令系统及…