JavaEE初阶Day 7:多线程(5)

目录

  • Day 7:多线程(5)
    • 1. 死锁
    • 2. 死锁场景
    • 3. 场景二:两个线程,两把锁
    • 4. 场景三:N个线程,M把锁
    • 5. 避免死锁问题
    • 6. 内存可见性问题

Day 7:多线程(5)

回顾synchronized

  • synchronized带有(),填写锁对象,锁对象存在的意义,只是起到“身份标识”效果
  • 两个线程是否是针对同一个对象加锁,如果是,就可能产生阻塞/锁竞争/锁冲突
  • synchronized{},进入代码块,就相当于加锁操作,出了代码块,就相当于解锁操作
  • 修饰普通方法,相当于针对this加锁,修饰静态方法,相当于针对类对象加锁

1. 死锁

package thread;class Counter2 {private int count = 0;void add() {synchronized (this) {count++;}}int get() {return count;}
}public class Demo21 {public static void main(String[] args) throws InterruptedException {Counter2 counter2 = new Counter2();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter2.add();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {synchronized (counter2) {counter2.add();}}});t2.start();t1.start();t2.join();t1.join();System.out.println("count = " + counter2.get());}
}

上述线程t2的代码相当于

Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {synchronized (counter2) {synchronized (counter2){count++;}}}
});

假设t2先启动(t1先不考虑),t2第一次加锁,肯定能加锁成功,当t2尝试第二次加锁的时候,此时counter2变量,属于已经被锁定的状态了,针对一个已经被锁定的对象加锁,就会出现阻塞等待,阻塞到对象被解锁为止

  • 要想获得到第二层的锁,就要执行完第一层的代码块
  • 要想执行完第一层代码块,就需要先获取到第二层的锁

这种情况下,就叫“死锁”

但是在实际上述过程中,对于synchronized是不适用的,synchronized上述代码是不会出现死锁的,但是如果是C++/Pyhton的锁就会出现死锁

  • synchronized在内部进行了特殊处理(JVM)
  • 每个锁对象里,会记录当前是哪个线程持有了这个锁,当针对这个对象加锁操作时,就会先判定一下,当前尝试加锁的线程,是否是持有同一锁的线程,如果不是,就阻塞,如果是,直接放行
  • 这种机制称为**“可重入锁”**,目的是为了避免程序员粗心大意,搞出死锁

注意:当加了多层锁的时候,代码执行到哪里要真正进行解锁呢

一定是在遇到最外层的},那么,如何确定遇到的}是最外层的,运行时,给锁对象里也维护一个计数器(int n),每次遇到{,n++(只有第一次才真正加锁),当遇到}就n–,当n减到0了,才真正解锁

2. 死锁场景

死锁有三种比较典型的场景

(1)场景一:锁是不可重入锁,并且一个线程针对一个锁对象,连续加锁两次,通过引入可重入锁,可以解决上述问题

(2)场景二:两个线程,两把锁

(3)场景三:N个线程,M把锁

3. 场景二:两个线程,两把锁

有线程1和线程2,以及锁A和锁B,现在线程1和2都需要获取到锁A和锁B(拿到锁A之后,不释放A,继续获取锁B),即先让两个线程分别拿到一把锁,然后去尝试获取对方的锁

举个例子:健康码崩了,程序员回到公司修复bug,被保安拦住了

  • 保安:出示健康码,才能进公司
  • 程序员:我得进公司修复bug,才能出示健康码

类似于:家钥匙锁车里了,车钥匙锁家里了

package thread;public class Demo22 {public static void main(String[] args) throws InterruptedException {Object locker1 = new Object();Object locker2 = new Object();Thread t1 = new Thread(() ->{synchronized (locker1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker2){System.out.println("t1 获取了两把锁");}}});Thread t2 = new Thread(()->{synchronized (locker2){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker1){System.out.println("t2 获取了两把锁");}}});t1.start();t2.start();t1.join();t2.join();}
}

上述代码:

  • t1尝试针对locker2加锁,就会阻塞等待,等待t2释放locker2
  • t2尝试针对locker1加锁,也会阻塞等待,等待t1释放locker1

在这里插入图片描述

当遇到死锁问题,可以通过上述调用栈+状态进行定位

4. 场景三:N个线程,M把锁

随着线程数目/锁的个数增加,此时情况就更复杂了,更容易出现死锁

哲学家就餐问题

现在桌子上均匀摆放有5根筷子,总共有5位哲学家,也就是说每位哲学家左右两边各一双筷子,每一位哲学家要做的事情就是放下筷子或者拿起左右两根筷子,但是每个哲学家什么时候放下筷子,什么时候拿起左右两根筷子是不确定的(抢占式执行)

如果出现下列极端情况,就相当于死锁了

  • 同一时刻,所有的哲学家都拿起左边的筷子,那么此时所有的哲学家都无法拿起右手的筷子
  • 假如哲学家都是比较固执的人,不能拿起两双筷子,就绝对不会放下手里的筷子

上述就是非常典型的死锁情况

死锁是非常严重的问题:死锁会使线程被卡住,没办法继续工作了,而且死锁这种bug,往往都是概率性出现

5. 避免死锁问题

死锁的四个必要条件

  • 锁具有互斥特性:这个是锁的基本特性,一个线程拿到锁之后,其他线程就得阻塞等待
  • 锁不可抢占(不可被剥夺):锁的基本特点,一个线程拿到锁之后,除非自己主动释放锁,否则别人抢不走
  • 请求和保持:属于代码结构层面,一个线程拿到一把锁之后,不释放这个锁的前提下,再尝试获取其他锁
  • 循环等待:属于代码结构层面,多个线程获取多个锁的过程中,出现了循环等待,A等待B,B又等待A

必要条件缺一不可,任何一个死锁的场景,都必须同时具备上述四点

当代码中,确实需要用到多个线程获取多把锁,一定要记得约定好加锁的顺序,就可以有效避免死锁了

6. 内存可见性问题

package thread;import java.util.Scanner;public class Demo23 {private static int count = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{while (count==0){}System.out.println("t1执行结束");});Thread t2 =new Thread(()->{Scanner scanner = new Scanner(System.in);System.out.println("请输入一个整数:");count = scanner.nextInt();});t1.start();t2.start();}
}

上述代码,当t2线程读到一个不为0的整数的时候,预期t1就会结束循环,但是结果并非如此

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

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

相关文章

Windows下使用SDKMAN对JDK(Java)进行多版本管理

Windows下使用SDKMAN对JDK&#xff08;Java&#xff09;进行多版本管理 1.背景2.基于msys2工具2.1. msys2简介2.2. 安装与配置2.2. Windows环境变量配置参考2.3 结果确认 3. 基于WSL 1.背景 前端有nvm,python有miniconda,miniforge等&#xff0c;java呢&#xff1f;java在Linu…

前端css笔记(pink老师)

css css书写顺序 自适应屏幕 html { width: 100%; height: 100%; display: table; } body { display: table-cell; } 用了这个方法以后&#xff0c;如果希望页面内的盒子也适应屏幕大小&#xff0c;则使用以下方法&#xff0c;会根据父亲的宽高计算出该盒子的宽高 width:xx%; …

java进阶---反射

获取class对象 Testvoid reflect() throws Exception {Class userClass User.class;System.out.println(userClass.getName()); // 全类名 com.zyw.proxydemo.pojo.UserSystem.out.println(userClass.getSimpleName()); // 简名 UserClass userClass1 Class.forName("co…

SpringBoot通过UUid实现文件上传接口及问题解决

在controller中&#xff0c;添加对应的方法体&#xff1a; PostMapping("/upload")ResponseBodypublic ApiRestResponse upload(HttpServletRequest httpServletRequest, RequestParam("file")MultipartFile file) throws IOException {String fileName f…

工业项目中你连PLM系统都没见过?

什么是 PLM 软件&#xff1f; PLM 软件是用于管理全球供应链中产品或服务全生命周期环节的解决方案。它包括从物料、零部件、产品、文档、规定、工程变更单到质量工作流的数据管理。 PLM 的发展历史 从最初的产品设计管理到如今的数字化转型和智能化生产&#xff0c;PLM 在不断…

tkinter窗口组件Entry

from tkinter import * 创建主窗口 app Tk() 设置窗口大小为1040x2048(手机) app.geometry(“1040x2048”) 设置窗口背景为灰色 app.configure(bg“gray”) 定义一个函数text()&#xff0c;用于处理输入框内容的变化 def text(): # 获取输入框e1的内容 if e1.get() “…

2024年阿里云优惠券领取和使用方法

阿里云优惠代金券领取入口&#xff0c;阿里云服务器优惠代金券、域名代金券&#xff0c;在领券中心可以领取当前最新可用的满减代金券&#xff0c;阿里云百科aliyunbaike.com分享阿里云服务器代金券、领券中心、域名代金券领取、代金券查询及使用方法&#xff1a; 阿里云优惠券…

基于ssm的大学生租房平台的设计与实现(java源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的大学生租房平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 大学生租房平台的设计与实现的主…

架构设计参考项目系列主题:新零售SaaS架构:客户管理系统架构设计

什么是客户管理系统? 客户管理系统,也称为CRM(Customer Relationship Management),主要目标是建立、发展和维护好客户关系。 CRM系统围绕客户全生命周期的管理,吸引和留存客户,实现缩短销售周期、降低销售成本、增加销售收入的目的,从而提高企业的盈利能力和竞争力。 …

linux——Bash特性

bash是一个命令解释器&#xff0c;其支持命令行展开&#xff5b;&#xff5d;写法 alias是命令别称&#xff0c;即为命令等同于&#xff0c;使用unalias对应命令可以取消该别称 alias可以对命令进行更改

紫光展锐T610平台_4G安卓核心板方案定制开发

紫光展锐T610核心板配备Android 11操作系统&#xff0c;采用12nm制程工艺。该处理器CPU由2颗基于Cortex-A75架构的大核心和6颗基于Cortex-A55架构的小核心组成&#xff0c;最高主频为1.8GHz。GPU采用的是614.4MHz的Mali G52&#xff0c;可以流畅播放2400*1080分辨率视频&#x…

淘宝批量采集商品详情数据(属性丨详情图丨sku丨价格等)

淘宝批量采集商品详情数据&#xff08;包括属性、详情图、SKU、价格等&#xff09;可以通过以下几种方式实现&#xff1a; 使用淘宝数据抓取工具&#xff1a;这类工具&#xff0c;如某鱼等&#xff0c;能够自动化采集淘宝商品数据&#xff0c;并将其转换成CSV、Excel等格式&am…

【C++ 学习】 priority_queue 优先队列的学习!!

1 queue****的介绍** 队列是一种容器适配器&#xff0c;专门用于在FIFO上下文(先进先出)中操作&#xff0c;其中从容器一端插入元素&#xff0c;另一端提取元素。 队列作为容器适配器实现&#xff0c;容器适配器即将特定容器类封装作为其底层容器类&#xff0c;queue提供一组特…

DVWA -File Upload-通关教程-完结

DVWA -File Upload-通关教程-完结 文章目录 DVWA -File Upload-通关教程-完结页面功能LowMediumHighImpossible 页面功能 此页面的功能为选择某个图片文件点击Upload按钮上传&#xff0c;上传成功后得知文件上传路径为DVWA\hackable\uploads。 Low 源码审计 这段 PHP 代码…

[面向对象] 单例模式与工厂模式

单例模式 是一种创建模式&#xff0c;保证一个类只有一个实例&#xff0c;且提供访问实例的全局节点。 工厂模式 面向对象其中的三大原则&#xff1a; 单一职责&#xff1a;一个类只有一个职责&#xff08;Game类负责什么时候创建英雄机&#xff0c;而不需要知道创建英雄机要…

《前端面试题》- JS基础 - 防抖和节流

在界面触发点击&#xff0c;滚动&#xff0c;输入校验等事件时&#xff0c;如果对事件的触发频率不加以限制&#xff0c;会给浏览器增加负担&#xff0c;且对用户不友好。防抖和节流就是针对类似情况的解决方案。 防抖 防抖(debounce)&#xff1a;当连续触发事件时&#xff0…

【AIGC】本地部署通义千问 1.5 (PyTorch)

今天想分享一下 Qwen 1.5 官方用例的二次封装&#xff08; huggingface 说明页也有提供源码&#xff09;&#xff0c;其实没有太多的技术含量。主要是想记录一下如何从零开始在不使用第三方工具的前提下&#xff0c;以纯代码的方式本地部署一套大模型&#xff0c;相信这对于技术…

两步解决 Flutter Your project requires a newer version of the Kotlin Gradle plugin

在开发Flutter项目的时候,遇到这个问题Flutter Your project requires a newer version of the Kotlin Gradle plugin 解决方案分两步: 1、在android/build.gradle里配置最新版本的kotlin 根据提示的kotlin官方网站搜到了Kotlin的最新版本是1.9.23,如下图所示: 同时在Ko…

【软考中级】软件设计师考点分布

文章目录 软考官网资格设置软考报考流程 【软件设计师】考点分布选择题考点分布案例题考点分布 软考官网 中国计算机技术职业资格网&#xff1a;https://www.ruankao.org.cn/ 官网报名平台&#xff1a;https://bm.ruankao.org.cn/sign/welcome 资格设置 计算机软件计算机网…

全栈的自我修养 ———— 如何发布一个npm包?

创建本地仓库 npm init在此期间会让你添加一些版本信息和名称 登陆npm npm login ——> yinhaodada arx.040208发布 npm publish查询