当前位置: 首页 > news >正文

【JavaEE】多线程(四)

多线程(四)

在开始讲之前,我们先来回顾回顾前三篇所讲过的内容~

  1. 线程的概念

    并发编程,多进程,比较重,频繁创建销毁,开销大

  2. Thread的使用

    1. 创建线程
      1. 继承Thread
      2. 实现Runnable
      3. 继承Thread(匿名内部类)
      4. 实现Runnable(匿名内部类)
      5. 使用lambda'
    2. Thread中的重要性
    3. 启动线程start
    4. 终止线程isInterrupted() interrupt()=>本质上是让线程快点执行完入口方法
    5. 等待线程join a.join()让调用这个方法的线程等待a线程的结束
    6. 获取线程引用
    7. 休眠线程
  3. 线程状态(方便快速判定当前程序执行的情况)

    1. NEW
    2. TERMINATED
    3. RUNNABLE
    4. TIMED_WAITING
    5. WAITING
    6. BLOCKED
  4. 线程安全

    1. 演示线程不安全的例子:两个线程自增5w次

    2. 原因:

      • 操作系统对于线程的调度是随机的
      • 多个线程同时修改同一个量
      • 修改操作不是原子性的
      • 内存可见性
      • 指令重排序
    3. 解决:加锁 => synchronized

      synchronized修饰的是一个代码块

      同时指定一个锁对象

      进入代码块的时候,对该对象进行加锁

      出了代码块的时候,对该对象进行解锁


      锁对象

      • 锁对象到底用哪个对象是无所谓的,对象是谁不重要;重要的是两线程加锁的对象是否是同一个对象

      • 这里的意义/规则,有且只有一个

        当两个线程同时尝试对一个对象加锁,此时就会出现“锁冲突”/“锁竞争”,一旦竞争出现,一个线程能够拿到锁,继续执行代码;一个线程拿不到锁,就只能阻塞等待,等待前一个线程释放锁之后,他才有机会拿到锁,继续执行~

      • 这样的规则,本质上就是把“并发执行” => “串行执行”,这样就不会出现“穿插”的情况了。


文章目录

  • 多线程(四)
    • synchronized 关键字
      • 互斥
      • 刷新内存
      • 可重入
    • 死锁
      • 死锁的成因

synchronized 关键字

互斥

续上文最后,synchronized除了修饰代码块之外,还可以修饰一个实例方法,或者一个静态方法

class Counter{public int count;synchronized public void increase(){count++;}public void increase2(){synchronized (this) {count++;}}synchronized public static void increase3(){}public static void increase4(){synchronized (Counter.class){}}
}
// synchtonized 使用方法
public class Demo14 {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 i = 0; i < 50000; i++) {counter.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(counter.count);}
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


synchronized用的锁是存在Java对象头里的。

何为对象头呢?

Java的一个对象,对应的内存空间中,除了你自己定义的一些属性之外,还有一些自带的属性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在对象头中,其中就会有属性表示当前对象是否已经加锁了


刷新内存

synchronized的工作过程:

  1. 获得互斥锁

  2. 从主内存拷贝变量的最新副本到工作的内存

  3. 执行代码

  4. 将更改后的共享变量的值刷新到主内存

  5. 释放互斥锁

但是目前刷新内存这一块知识各种说法都有,目前也难以通过实例验证,pass~


可重入

synchronized:重要的特性,可重入的

所谓的可重入锁,指的就是,一个线程连续针对一把锁,加锁两次,不会出现死锁。满足这个需求就是“可重入锁”,反之就是“不可重入锁”。

下面见图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述的现象,很明显就是一个bug,但是我们在日常开发中,又难以避免出现上述的代码~例如下面这样的案例:

public class Demo15 {private static Object locker = new Object();public static void func1(){synchronized (locker){func2();}}public  static void func2(){func3();}public static void func3(){func4();}public static void func4(){synchronized (locker){}}public static void main(String[] args) {}
}

要解决死锁问题,我们可以将synchronized设计成可重入锁,就可以有效解决上述的死锁问题~

就是让锁记录一下,是哪个线程给它锁住的,后续再加锁的时候,如果加锁线程就是持有锁的线程,就直接加锁成功~

用一个例子来理解:

你向一个哥们表白,我爱你,成功了,他接受你了,也就是你对他加锁成功了,同时他也会记得你就是她的男朋友~

过了几天,你又对他说,宝贝我爱你,这时候的那个哥们当然也不会拒绝,反而会更加基情~

不过要是换成别人,结果肯定就是不一样的(排除绿你的情况~)


这里提出个问题:

synchronized(locker){synchronized(locker){........................}}
  1. 在上述代码中,synchronized是可重入锁,没有因为第二次加锁而死锁,但是当代码执行到 }②,此时锁是否应该释放?

**不能!!!**因为如果释放了锁,很可能就会导致②和①之间的一些代码逻辑无法执行,也就起不到锁保护代码的作用了~

  1. 进一步,如果上述的锁有n层,释放时机该怎么判断?

无论此处有多少层,都是要在最外层才能释放锁~~
引用计数
锁对象中,不光要记录谁拿到了锁,还要记录,锁被加了几次
每加锁一次,计数器就+1.
每解锁一次,计数器就·1.
出了最后一个大括号,恰好就是减成0了,才真正释放锁


死锁

那么上面我们讲解了死锁的一种情况,一个线程针对一把锁,加锁两次。

接下来下面我们继续介绍死锁的情况~

  1. 一个线程针对一把锁,加锁两次,如果是不可重入锁,就会死锁~

    synchronized不会出现,但是隔壁C++的std::mutex就是不可重入锁,就会出现死锁)

  2. 两个线程(t1、t2),两把锁(A、B)(此时无论是不是不可重入锁,都会死锁)

    举个例子:钥匙锁车里,车钥匙锁家里~

    1. t1获取锁A,t2获取锁B
    2. t1尝试获取B,t2尝试获取A

    实例代码

    // 死锁
    public class Demo16 {private static Object locker1 = new Object();private static Object locker2 = new Object();//此处的sleep很重要,要确保 t1 和 t2 都分别拿到一把锁之后,再进行后续动作public static void main(String[] args) {Thread t1 = new Thread(()->{synchronized (locker1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker2){System.out.println("t1 加锁成功");}}});Thread t2 = new Thread(()->{synchronized (locker2){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker1){System.out.println("t2 加锁成功");}}});t1.start();t2.start();}
    }
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    死锁现象出现

    我们可以在jconsole.exe中看看线程情况~

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    同时也要注意,死锁代码中
    两个synchronized嵌套关系,不是并列关系.
    嵌套关系说明:是在占用一把锁的前提下,获取另一把锁.(则是可能出现死锁)
    并列关系,则是先释放前面的锁,再获取下一把锁.(不会死锁的)

  3. N个线程,M把锁(相当于2的扩充)

    此时这个情况,更加容易出现死锁了。

    下面给出一个经典例子:哲学家就餐问题

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    死锁,是属于比较严重的bug,会直接导致线程卡住,也就无法执行后续的工作了~

    那么我们应该怎么避免死锁?

死锁的成因

那么首先我们要了解死锁的成因:

  1. 互斥使用。(锁的基本特性)

    当线程持有一把锁之后,另一个线程也想获取到锁,那么就需要阻塞等待、

  2. 不可抢占。(锁的基本特性)

    当锁已经被 线程 1 拿到之后,线程 2 只能等 线程 1 主动释放,不可以强行抢过来

  3. 请求保持。(代码结构)

    一个线程尝试获取多把锁。(先拿到 锁1 之后,再尝试获取 锁2 ,获取的时候, 锁1 不会被释放)

    这种也就是典型的吃着碗里的,看着锅里的

    public class Demo16 {private static Object locker1 = new Object();private static Object locker2 = new Object();//此处的sleep很重要,要确保 t1 和 t2 都分别拿到一把锁之后,再进行后续动作public static void main(String[] args) {Thread t1 = new Thread(()->{synchronized (locker1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker2){System.out.println("t1 加锁成功");}}});Thread t2 = new Thread(()->{synchronized (locker2){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker1){System.out.println("t2 加锁成功");}}});t1.start();t2.start();}
    }
    
  4. 循环等待 / 环路等待(代码结构)

    等待的依赖关系,形成环了~

    也即是上面那个例子,钥匙锁车里,车钥匙锁家里

实际上,要想出现死锁,也不是个容易事情
因为得把上面4条都占了.
(不幸的是,1和2是锁本身的特性,只要代码中,把3和4占了,死锁就容易出现了)

所以说,解决死锁,核心就是破坏上述必要条件,死锁就形成不了~

针对上述的四种成因,1 2是破坏不了的,因为synchronized自带特性,我们是无法干预 滴~

对于3来说,就是调整代码结构,避免编写“锁嵌套”逻辑

对于4来说,可以约定加锁的顺序,就可以避免循环等待


所以针对上面的哲学家就餐问题,我们可以采取:针对锁进行编号

比如说约定,加多一把锁的时候,先加编号小的锁,后加编号大的锁(所有线程都要遵守这个规则)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样的话,循环等待就会被解除,死锁也不会出现了~

回到上述我们讲的synchronized关键字
在使用规则上,并不复杂,只要抓住一个原则:两个线程针对同一个对象加锁,就会产生锁竞争.
但是在底层原理上,synchronized还有不少值得讨论的地方.接下来会展开讲讲~


至此,多线程(四)讲解到这,接下来会持续更新,敬请期待~

相关文章:

【JavaEE】多线程(四)

多线程&#xff08;四&#xff09; 在开始讲之前&#xff0c;我们先来回顾回顾前三篇所讲过的内容~ 线程的概念 并发编程&#xff0c;多进程&#xff0c;比较重&#xff0c;频繁创建销毁&#xff0c;开销大 Thread的使用 创建线程 继承Thread实现Runnable继承Thread&#xff…...

第一章:最新版零基础学习 PYTHON 教程(第七节 - Python 中的语句、缩进和注释)

在这里,我们将讨论Python中的语句、Python中的缩进和Python中的注释。我们还将讨论 Python 语句、Python 缩进、Python 注释的不同规则和示例,以及“文档字符串”和“多行注释”之间的区别。 Python中的语句是什么 Python语句是Python 解释器可以执行的指令。Python 语言中…...

C++ 【2】

1.指针基础 字符&#xff1a;C 一个字符占一个字节 在C中 << 这个为插入运算符 >> 这个为提取运算符 一个变量的地址称为该变量的指针&#xff1b;如果在程序中定义了一个变量或者数组&#xff0c; 那么&#xff0c;这个变量或数组的地址&#xff08;指针…...

Java学习笔记40——Lambda表达式

Lambda表达式 Lambda表达式函数式编程思想概述Lambda表达式的标准格式Lambda表达式练习练习1练习2练习3 Lambda表达式的省略模式Lambda表达式的注意事项Lambda表达式与接口的区别 Lambda表达式 函数式编程思想概述 面向对象思想强调“必须通过对象的形式做事” 在函数式思想中…...

【考研数学】线性代数第五章 —— 特征值和特征向量(3,矩阵对角化理论)

文章目录 引言三、矩阵对角化理论3.1 一般矩阵的相似对角化3.2 实对称矩阵的相似对角化3.2.1 实对称矩阵相似对角化定理3.2.2 实对称矩阵相似对角化过程 写在最后 引言 承接前文&#xff0c;我们来看看矩阵的对角化理论。 我们前面提到对角化是在矩阵相似那里&#xff0c;若存…...

【计算机网络】IP数据报首部格式、最大传输单元MTU、最大分段大小MSS

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多计算机网络知识专栏&#xff1a;计算机网络&#x1f525; 给大家跳段…...

shell脚本之文件读写

shell脚本之文件读写 1、读取文件内容并打印2、将内容写入文件3、追加内容到文件末尾4、读取文件内容到变量中5、逐行读取文件内容并处理6、逐行追加内容到文件末尾7、获取文件行数8、获取文件最后一个单词 Shell 脚本读写文件的方法有很多种&#xff0c;下面是一些常见的方法&…...

SAP 刷新Fiori Apps缓存的方法(解决修改CDS后Fiori无法重载新配置)

1. 问题 修改CDS Annotation后&#xff0c;基于Fiori Element发布的Fiori App无法应用新的界面 2. 解决办法 2319491 - How to clean up the cache after applying changes that affect SAP Fiori apps刷新Frontend&#xff1a; SE38&#xff1a; /UI5/APP_INDEX_CALCULATE…...

如何在 Excel 中计算日期之间的天数

计算两个日期之间的天数是 Excel中的常见操作。无论您是规划项目时间表、跟踪时间还是分析一段时间内的趋势&#xff0c;了解如何在 Excel 中查找日期之间的天数都可以提供强大的日期计算功能。 幸运的是&#xff0c;Excel 提供了多种简单的方法来获取两个日期之间的天数。继续…...

Java高级-注解

注解 1.介绍2.元注解3.注解的解析4.注解的应用场景 1.介绍 注解 Annotation 就是Java代码里的特殊标记&#xff0c;作用是让其他程序根据注解信息来决定什么是执行该程序注解&#xff1a;注解可以在类上、构造器上、方法上、成员变量上、参数上等位置 自定义注解 /*** 自定…...

wabp.m 代码注释(便于算法快速理解)

算法效果: 波峰和起点检测效果: function [r,pk] = wabp(Araw, Offset,Scale, Fs) % r = wabp(Araw,Offset,Scale, Fs); % Input: Araw (125Hz sampled) waveform in wfdb-MIT format, % Offset, Scale % Araw = 血压波形 % Offset=偏移(信号减去或者加上偏移恢复成…...

数据库数据恢复-SQL SERVER数据库文件损坏的故障表现数据恢复方案

SQL SERVER数据库故障类型&#xff1a; SQL SERVER数据库MDF&#xff08;NDF&#xff09;或LDF损坏。 SQL SERVER数据库故障原因&#xff1a; 1、数据库正在操作过程中&#xff0c;机器突然断电。 2、人为误操作。 SQL SERVER数据库MDF&#xff08;NDF&#xff09;或LDF损坏的…...

flink中cpu消耗的大户-序列化和反序列化

背景 故事的起源来源于这样一篇关于序列化/反序列化优化的文章https://www.ververica.com/blog/a-journey-to-beating-flinks-sql-performance,当把传输的对象从String变成byte[]数组后&#xff0c;QPS直接提升了50% flink的网络数据交换优化 在flink中对于每个算子之间的跨…...

使用 K 均值聚类进行颜色分割

介绍 颜色分割是计算机视觉中使用的一种技术,用于根据颜色识别和区分图像中的不同对象或区域。聚类算法可以自动将相似的颜色分组在一起,而不需要为每种颜色指定阈值。当处理具有大范围颜色的图像时,或者当事先不知道确切的阈值时,这非常有用。 在本教程中,我们将探讨如何…...

Redis 哈希表操作实战(全)

目录 HSET 添加 HSETNX 添加 HMSET 批量添加 HGET 获取元素 HGETALL 获取所有 HMGET 批量查询 HEXISTS 判断是否存在 HINCRBY 增加整数 HINCRBYFLOAT 添加浮点数 HLEN 查Field数量 HKEYS 查所有Field HVALS 查所有Field值 HSCAN 迭代 HDEL 删除Field HSET 添加 …...

element table合并行或列 span-method

首先来看下官网上如何写的 <template><div><el-table:data"tableData":span-method"objectSpanMethod"borderstyle"width: 100%; margin-top: 20px"><el-table-columnprop"id"label"ID"width"18…...

【操作系统笔记】内存分配

内存对齐 问题&#xff1a;为什么需要内存对齐呢&#xff1f; 主要原因是为了兼容&#xff0c;为了让程序可以运行在不同的处理器中&#xff0c;有很多处理器在访问内存的时候&#xff0c;只能从特定的内存地址读取数据。换个说法就是处理器每次只能从内存取出特定个数字节的数…...

Web 整合

HTML span 行内元素 p 块级元素 br/ 强制换行 i em倾斜 b strong 加粗 u 下划线 mark 高亮 超链接 a &#xff1a;a href"链接地址" target"_blank" alt"可替文本" title"文字提示" tartget&#xff1a;_self 自己界面打开 _…...

hasOwnProperty 方法解析

一、含义&#xff1a; hasOwnProperty 是 JavaScript 中的一个内置方法&#xff0c;用于检查对象是否具有指定名称的属性。 具体来说&#xff0c;hasOwnProperty 方法用于判断一个对象是否拥有某个指定的属性&#xff0c;而不是继承自原型链的属性。它是一个布尔值方法&#…...

使用 nohup 运行 Python 脚本

简介&#xff1a;在数据科学、Web 开发或者其他需要长时间运行的任务中&#xff0c;我们经常需要让 Python 脚本在后台运行。尤其是在远程服务器上&#xff0c;可能因为网络不稳定或需要执行多个任务&#xff0c;我们不希望 Python 脚本因为终端关闭而被终止。这时&#xff0c;…...

Django:五、登录界面实现动态图片验证码

一、下载包 pip install pillow 二、代码 这是一个函数&#xff0c;无输入&#xff0c;返回两个值。一个值是图片&#xff0c;一个值是图片中的数字及字母。 需要注意&#xff1a;font_fileMonaco.ttf 是一个验证码字体文件&#xff0c;如有需要&#xff0c;可三连私信。 …...

GPT,GPT-2,GPT-3,InstructGPT的进化之路

ChatGPT 火遍圈内外&#xff0c;突然之间&#xff0c;好多人开始想要了解 NLP 这个领域&#xff0c;想知道 ChatGPT 到底是个什么&#xff1f;作为在这个行业奋斗5年的从业者&#xff0c;真的很开心让人们知道有一群人在干着这么样的一件事情。这也是我结合各位大佬的文章&…...

firefox_dev_linux下载安装配置(部分系统自带包请看结尾)

download 从 Firefox 的官方网站下载 Firefox Developer Edition 的 tar 文件 firefox_dev_linux_download # 终端快速下载 wget https://download.mozilla.org/?productfirefox-devedition-latest-ssl&oslinux64&langen-US彻底删除自带原版 # apt系 sudo apt --pu…...

vim缓存-交换文件

Catf1agCTF靶场 web swp 题目链接&#xff1a;http://catf1ag.cn/ 个人博客&#xff1a;https://sword-blogs.com/ 题目考点&#xff1a; vim在编辑文档的过程中如果异常退出&#xff0c;会产生缓存文件 vim 交换文件名 参考文章&#xff1a;vim手册 https://yianwillis.…...

Hive 优化建议与策略

目录 ​编辑 一、Hive优化总体思想 二、具体优化措施、策略 2.1 分析问题得手段 2.2 Hive的抓取策略 2.2.1 策略设置 2.2.2 策略对比效果 2.3 Hive本地模式 2.3.1 设置开启Hive本地模式 2.3.2 对比效果 2.3.2.1 开启前 2.3.2.2 开启后 2.4 Hive并行模式 2.5 Hive…...

CentOS 7.5 centos failed to load selinux policy 错误解决方法

这是个 selinux 使能导致的&#xff0c; 关闭即可 在进入到内核选中界面&#xff0c;选中要启动的内核&#xff0c; 按键盘 e 就会进入启动参数界面 进入启动参数界面如图&#xff0c;按上下键找到 UTF8 UTF8如图&#xff0c; 添加 selinux0 添加完成如图&#xff0c; 按 ctr…...

注入之SQLMAP(工具注入)

i sqlmap是一个自动化的SQL注入工具&#xff0c;其主要功能是扫描&#xff0c;发现并利用给定的URL和SQL注入漏洞&#xff0c;其广泛的功能和选项包括数据库指纹&#xff0c;枚举&#xff0c;数据库提权&#xff0c;访问目标文件系统&#xff0c;并在获取操作权限时执行任…...

Linux学习资源Index

由于Linux是支撑“云计算”的最核心、最底层、最重要的技术&#xff0c;持续提升自已的Linux水平是必须的&#xff0c;这里将不断更新的Linux学习索引。 书籍 书籍首页 - Documentation (rockylinux.org) WWW链接 提定发行版 RockyLinux Rocky Linux Download Rocky | R…...

什么是SVG(可缩放矢量图形)?它与普通图像格式有何不同?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是SVG&#xff1f;⭐ 与普通图像格式的不同⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚…...

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows 大家好我是艾西&#xff0c;最近研究了下 l4d2&#xff08;求生之路2&#xff09;这款游戏的搭建以及架设过程。今天就给喜欢l4d2这款游戏的小伙伴们分享下怎么搭建架设一个自己的服务器。毕竟自己当服主是热爱游…...

好看的网站界面设计/关键词优化搜索排名

导读在上篇中&#xff0c;我们已经创建了一个 shell 主循环、切分了命令输入&#xff0c;以及通过 fork 和 exec 执行命令。在这部分&#xff0c;我们将会解决剩下的问题。首先&#xff0c;cd test_dir2 命令无法修改我们的当前目录。其次&#xff0c;我们仍无法优雅地从 shell…...

镇江网站建设联系思创/sem是什么电镜

在总部位于新加坡的员工福利初创公司CXA Group对我们的核心Web平台进行评估的过程中&#xff0c;我们决定将方向从陈旧的现有体系结构转向&#xff0c;从头开始重建前端。 该平台面临的挑战之一是创建一个在CXA Group的12个目标国家/地区&#xff08;在整个亚洲&#xff09;都能…...

建设网站需要什么资质/宁波seo优化公司排名

本篇介绍easymybatis如配置乐观锁和逻辑删除。乐观锁easymybatis提供的乐观锁使用方式跟JPA一样&#xff0c;使用Version注解来实现。即&#xff1a;数据库增加一个int或long类型字段version&#xff0c;然后实体类version字段上加上Version注解即可。实现原理是根据mysql的行锁…...

虚拟网站建设/百度广告上的商家可靠吗

以下是在制定测试策略时要考虑的最常见的软件测试类型列表以及测试说明&#xff1a; 功能测试 - 这种类型的测试侧重于用户的体验。从测试 代码 的小组件到 UI 的完整端到端测试&#xff0c;功能测试可确保您的应用程序按预期工作。它有助于防止限制用户访问您的应用程序的问题…...

wordpress管理网址/温州seo优化

将原string 中的元素或子串替换。返回替换后的string。 &#xff08;1&#xff09;用string 或C-string 代替操作string 中从 _Pos1 开始的 _Num1 个字符 basic _ string& replace( size _ type _Pos1 ,size _ type _Num1 , const value _ type* _Ptr ); basic _ str…...

大连疫情最新通报/南昌seo外包公司

重庆交通大学学生实验报告实验课程名称开课实验室学院年级开课时间至学年第学期实验一基本指令的编程练习一、实验目的1、熟悉plc实验装置&#xff0c;s7-200系列编程控制器的外部接线方法。2、了解编程软件step7的编程环境&#xff0c;软件的使用方法。3、掌握与、或、非逻辑功…...