【JavaEE初阶】多线程(3)

devtools/2024/9/20 3:52:08/ 标签: java, 开发语言

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗~

如有错误,欢迎指出~



目录

线程状态

线程安全

代码示例 

解释

总结原因

解决方案-->加锁

t1和t2都加锁 且 同一个锁对象

t1和t2中只有一个加锁了

t1和t2都加锁,但锁对象不同

加锁 与线程等待(join) 的区别

使用类对象加锁

加锁场景

死锁

场景1

场景2

​编辑场景3

构成死锁的4个必要条件(缺一不可)

解决方案举例

方案1 避免锁嵌套

方案2 约定加锁顺序

方案3 银行家算法


线程状态

进程状态 分为两种:

  • 就绪:正在cpu上执行,或者随时可以去cpu上执行
  • 阻塞:暂时不能参与cpu执行

Java的线程状态有 6 种

  1. NEW  当前Thread对象虽然有了,但是内核的线程还没有(还没调用start)
  2. TERMINATED 当前Thread对象虽然还在,但是内核线程已经销毁(线程已经结束)
  3. RUNNABLE  就绪状态,正在cpu上运行 +  随时可以去cup上运行
  4. BLOCKED  因为锁竞争引起的阻塞
  5. TIMED_WAITING  有超时的等待 如sleep,或者join带参数版本
  6. WAITING  没有超时 时间的阻塞等待 如 join/wait

学习线程状态主要为了 调试,比如 遇到某个代码功能没有执行,就可以观察对应线程的状态,看是否是因为一些原因阻塞了.

线程安全

多个线程同时执行某个代码时,可能会引起一些奇怪的bug,理解线程安全才能 避免/解决 上述bug

代码示例 

public class Demo12 {public static int count=0;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="+ count);}
}

因为多线程并发执行 引发的bug称为"线程安全问题" 或"线程不安全"

解释

上述代码count++;在cpu视角是3个指令:

  1. load   把内存中的数据读到cpu寄存器里
  2. add    把cpu寄存器里的数据+1
  3. sava  把寄存器的值,写回内存

指令是cpu执行的基本单位,如果要调度,cup至少会把当前指令执行完,

cpu调度执行线程的方式是 抢占式执行,随机调度,

但是由于count++ 是三个指令,可能会出现cpu执行了其中1个指令或2个指令或3个指令就被调度走的情况(都有可能,无法预测), 所以 两个线程同时对count进行++ 就容易出现bug

由于循环5w次过程中不知道有多少次的执行顺序是前两种正确情况,有多少次是其他错误情况,最终的结果就是一个不确定的值,而这个值 一定小于10w

但结果也有可能小于5w

总结原因

  • 线程在操作系统中,随机调度,抢占式执行(根本原因), 此原因无法干预(操作系统内核,作为应用层的程序员无法干预)
  • 多个线程,同时修改一个变量(如果是一个线程修改,就没事)
  • 修改操作,不是"原子"的,(对cpu来说,一条指令才是"原子"的,是不可分割的最小的单位)

解决方案-->加锁

解决线程安全问题,最主要的方法就是 把"非原子" 的修改,变成"原子"(通过加锁,把非原子的修改操作 打包成一个整体,变成原子操作)

t1和t2都加锁 且 同一个锁对象

此处的加锁,没有干预到线程的调度,只是通过加锁,使一个线程在执行count++时,其他线程的count++不能插队进来

Java提供synchronized关键字 来完成加锁操作,synchronizede()的'()'中需要指定一个 "锁对象" (可以指定任何对象)来进行后面的判定

t1和t2都是针对locker对象加锁,t1加锁成功后,继续执行{}里的代码,t2后加锁,发现locker对象已经被别人先锁了,t2只能排队等待(这两者的++ 操作不会并发执行了,本质上是把随机并发的执行过程 强制变成了串行,从而解决了刚才的线程安全问题)

t1和t2中只有一个加锁了

t1和t2都加锁,但锁对象不同

锁对象的作用 就是用来区分 两个线程或多个线程 是否针对"同一个对象"加锁

  • 若是,此时就会出现阻塞(锁竞争/锁冲突)
  • 若不是,此时不会出现"阻塞",两个线程仍然是 随机调度的并发执行.

锁对象,只要是Object(或者其子类)都行,不能是int,double这样的内置类型

加锁 与线程等待(join) 的区别

上述加锁后的代码 本质上要比join的串行执行 的效率还要高

  • 加锁只是把线程中一小部分逻辑 变成了 串行执行,剩下其他部分仍然可以并发执行
  • join是 线程 整体都串行执行

使用类对象加锁

一个Java进程中,一个类的类对象是只有唯一一个的,类对象,也是对象,也可以成为锁对象,写类对象和写其他对象 没有本质区别,换句话说,写成类对象,就是'偷懒'的做法(不想单独创建锁对象了~)

 

加锁场景

是否要加锁,怎么加锁,都是和具体场景直接相关的("无脑加锁"是不推荐的)

锁 ,需要的时候才使用,不需要的时候不要使用,否则会付出代价(性能)

使用锁,就可能会发生阻塞,一旦某个线程阻塞,啥时候恢复阻塞 继续执行是不可预期的~

死锁

场景1

一个线程,针对一把锁,连续加锁两次

Java的synchronized做了特殊处理(引入了特殊机制,"可重入锁"),不会出现 死锁,但同样的代码换成c++/python就会死锁

可重入锁就是在锁中 额外记录一下 当前是哪个线程,对哪个锁加锁了,后续加锁时就会进行判定

还会引入一个引用计数,维护当前已经加锁几次了,并且描述何时真正释放锁

场景2

两个线程,两把锁

  1. 线程1先针对A加锁,线程2针对B加锁
  2. 线程1不释放锁A的情况下,在针对B加锁,同时线程2不释放锁B的情况下对A加锁

这种情况,可重入锁 也无能为力~

package thread;public class Demo17 {private static Object locker1 = new Object();private static Object locker2 = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (locker1) {System.out.println("t1 加锁 locker1 完成");// 这里的 sleep 是为了确保, t1 和 t2 都先分别拿到 locker1 和 locker2 然后在分别拿对方的锁.// 如果没有 sleep 执行顺序就不可控, 可能出现某个线程一口气拿到两把锁, 另一个线程还没执行呢, 无法构造出死锁.try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2) {System.out.println("t1 加锁 locker2 完成");}}});Thread t2 = new Thread(() -> {synchronized (locker2) {System.out.println("t2 加锁 locker2 完成");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1) {System.out.println("t2 加锁 locker1 完成");}}});t1.start();t2.start();}
}

场景3

N个线程,M个锁 

经典问题:哲学家就餐问题

当每根筷子都被哲学家左手拿起来了,他们右手就没有筷子可以拿了,当哲学家吃不到面条时,也就不会放下左手的筷子, 此时就产生了 死锁.

构成死锁的4个必要条件(缺一不可)

锁的基本特性:

  • 1.锁是互斥的 . 如 一个线程拿到锁,另一个线程就拿不到这个锁
  • 2.锁是不可被抢占的.  如 线程1拿到了锁A,若线程1不主动释放A,线程2不能把锁A抢过来

对于synchronized 这样的锁,互斥和不可抢占都是基本特性, 无法干预

代码结构上:

  • 3.请求 和 保持. 如 线程1拿到锁A之后,不释放A的前提下,去拿锁(解决方法: 如果是 先释放A,再拿B,不会有问题)
  • 4.循环等待 / 环路等待 / 循环依赖. 如 多个线程获取锁时,存在 循环等待( 解决方法:如果在获取多把锁的时候,不要构成循环等待就行了)

解决方案举例

针对场景2,通过改变代码结构 解决死锁问题(对症下药~)

方案1 避免锁嵌套

针对 死锁构成条件3 

方案2 约定加锁顺序

针对 死锁构成条件4 

给锁进行编号1,2,3,4...N,约定所有的线程在加锁时都必须按照一定的顺序加锁(比如,必须先针对编号小的锁,加锁,后对大的加锁)

方案3 银行家算法

但是银行家算法太复杂了,如果在日常开放中,实现一套银行家算法解决死锁,先不说死锁的问题是否存在,你实现的银行家算法本身可能存在bug~


http://www.ppmy.cn/devtools/109038.html

相关文章

CTFHub技能树-Git泄漏-Log

目录 一、前提知识 1.git泄漏原理 ​编辑 2.git文件泄漏造成后果 3.利用方法 (1) GitHack是一个.git泄露利用脚本&#xff0c;通过泄露的.git文件夹下的文件&#xff0c;还原重建工程源代码。渗透测试人员、攻击者&#xff0c;可以进一步审计代码&#xff0c;挖掘&#x…

深入理解Oracle数据库中的数据库链接

在Oracle数据库环境中&#xff0c;数据库链接&#xff08;Database Link&#xff09;是一种强大的特性&#xff0c;它允许用户从一个数据库&#xff08;本地数据库&#xff09;访问另一个数据库&#xff08;远程数据库&#xff09;中的数据。这种链接机制极大地增强了数据库的互…

简述CCS平面线性光源

光源在机器视觉系统中起着重要作用&#xff0c;不同环境、场景及应用合适光源都不一样&#xff0c;今天我们来看看LFX3-PT系列平面线性光源。它是最适合检测镜面物体的凹凸,外壳小巧的光源。备有根据检测条件可选的2种线间距。1mm型&#xff08;型号末尾&#xff1a;A&#xff…

每日一练:合并区间

一、题目要求 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;in…

BLIP3技术小结(xGen-MM (BLIP-3): A Family of Open Large Multimodal Models)

paperhttps://www.arxiv.org/abs/2408.08872githubhttps://github.com/salesforce/LAVIS/tree/xgen-mmOrg.Salesforce AI Research个人博客地址http://myhz0606.com/article/blip3 前置阅读&#xff1a;BLIP系列总结 核心思路 虽然过去BLIP系列对LMM发展起到至关重要的作用&…

@Value读取properties中文乱码解决方案

前几天碰到使用Value中文乱码的问题&#xff0c;英文字符则不会出现问题 原因&#xff1a;SpringBoot在加载properties配置文件时&#xff0c;使用的默认编码是&#xff1a;ISO_88599_1 解决方式&#xff1a;将properties改成yml就可以读取成功了 Data Component PropertySou…

R 包管理

R 包管理 函数总结简介安装包选择安装路径 更新包devtoolsGitHub 和 BioConductor加载包迁移扩展包 2024-09-04 函数总结 函数功能install.packages()安装包。不加参数&#xff0c;显示CRAN镜像站点站点&#xff0c;加包名称&#xff0c;直接下载安装包installed.packages()列…

EmguCV学习笔记 VB.Net 第10章 人脸识别

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

仿华为车机UI--图标从Workspace拖动到Hotseat同时保留图标在原来位置

基于Android13 Launcher3,原生系统如果把图标从Workspace拖动到Hotseat里则Workspace就没有了&#xff0c;需求是执行拖拽动作后&#xff0c;图标同时保留在原位置。 实现效果如下&#xff1a; 实现思路&#xff1a; 1.如果在workspace中拖动&#xff0c;则保留原来“改变图标…

项目日志——日志等级类和日志消息类的设计、实现、测试

文章目录 日志等级类设计实现测试 日志消息类设计实现 日志等级类 设计 日志等级一共分7个等级 UNKNOW 0OFF 关闭所有日志输出DEBUG 调试等级INFO 提示等级WARN 警告等级ERROR 错误等级FATAL 致命等级OFF 关闭所有日志的输出 每一个项目都会设置一个默认输出等级&#xff…

前端项目开发之prettier安装和使用

前端项目开发之安装prettier和使用 Prettier 是一个流行的代码格式化工具&#xff0c;可以帮助你保持代码风格的一致性。以下是如何在 Visual Studio Code (VS Code) 中安装和使用 Prettier 的步骤&#xff1a; 安装 Prettier 通过 VS Code 扩展市场安装&#xff1a; 打开 V…

使用AI赋能进行软件测试-文心一言

1.AI赋能的作用 提高速度和效率缺陷预测与分析 2.AI互动指令格式--文心一言 角色、指示、上下文例子、输入、输出 a 直接问AI 针对以下需求&#xff0c;设计测试用例。 需求&#xff1a; 1、账号密码登录系统验证账号和密码的正确性。 验证通过,用户登录成功,进入个人中心;验…

pytorch tensor.expand函数介绍

在 PyTorch 中&#xff0c;tensor.expand()是一个用于扩展张量维度的函数。 一、函数作用 它允许你在不复制数据的情况下&#xff0c;将张量的形状扩展到指定的维度大小。这对于需要在特定维度上重复数据的操作非常有用&#xff0c;例如在进行广播操作时调整张量的形状。 二…

Web3社交新经济,与 SOEX 实现无缝交易的高级安全性

出于充分的理由&#xff0c;安全性是交易中至关重要的考虑因素。每个人都应该确保自己的资金在交易时是安全的。由于 &#xff33;&#xff2f;&#xff25;&#xff38; 充当您与交易所的最佳连接&#xff0c;因此必须强调的是&#xff0c;该系统不会引发任何安全问题。 &a…

com.baomidou.mybatisplus.annotation.DbType 无法引入

com.baomidou.mybatisplus.annotation.DbType 无法引入爆红 解决 解决 ❤️ 3.4.1 是mybatis-plus版本&#xff0c;根据实际的配置→版本一致 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-annotation</artifactId>&…

[动态规划] 删除并获得点数

给你一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#xff0c;选择任意一个 nums[i] &#xff0c;删除它并获得 nums[i] 的点数。之后&#xff0c;你必须删除 所有 等于 nums[i] - 1 和 nums[i] 1 的元素。 开始你拥有 0 个点数。返回你能通过这些操…

国内短剧系统怎么搭建以及都需要那些资质?

聊到国内短剧&#xff0c;相信大家都不陌生&#xff0c;在各大短视频平台可谓是火的一批&#xff0c;您或许有想加入进来的想法&#xff0c;或是已经有规划还未实现的&#xff0c;请停下脚步&#xff0c;耐心看完该文章&#xff0c;相信一定会对你有所帮助的。本文介绍短剧平台…

做饭时用什么样的白酒能更好衬托食物的鲜味?

在做饭的时候&#xff0c;白酒扮演着举足轻重的角色&#xff0c;其核心功能在于祛除食材不良风味并显著提升菜肴的香醇层次。挑选适宜的白酒时&#xff0c;需细致考量其种类与酒精浓度&#xff0c;尽量与食材的风味和谐共生&#xff0c;而非相互抵触。以下是酱酒亮哥yutengtrad…

LeetCode HOT100系列题解之最大正方形(6/100)

题目&#xff1a;最大正方形. - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a; 第一种方法&#xff1a;前缀和二分答案&#xff08;暴力优化&#xff09;我感觉比官方给的暴力好一点 时间复杂度&#xff1a; 暴力优化1&#xff1a;通过前缀和减少判断1出现得次数…

SpringBoot教程(十五) | SpringBoot集成RabbitMq(消息丢失、消息重复、消息顺序、消息顺序)

SpringBoot教程&#xff08;十五&#xff09; | SpringBoot集成RabbitMq&#xff08;消息丢失、消息重复、消息顺序、消息顺序&#xff09; RabbitMQ常见问题解决方案问题一&#xff1a;消息丢失的解决方案&#xff08;1&#xff09;生成者丢失消息丢失的情景解决方案1&#xf…