23. Java 锁的可重入性验证

1. 前言

本节内容主要是对 Java 锁的可重入性进行验证,锁的可重入性的设计是避免死锁非常好的设计思想。本节内容的知识点如下:

  • 什么是锁的可重入性,这是本节课程的基础内容;
  • 了解可重入锁与非可重入性锁的不同之处,以凸显可重入性锁的优势所在,为本节基础内容;
  • 了解什么情况下使用可重入锁,是本节的重点内容之一;
  • synchronized 关键字验证锁的可重入性试验,为本节核心内容之一;
  • ReentrantLock 验证锁的可重入性试验,为本节核心内容之一;

其实 synchronized 关键字与 ReentrantLock 都是 Java 常见的可重入锁,本节内容使用 ReentrantLock 和 synchronized 来讲解锁的可重入性。

2. 什么是锁的可重入性

定义:可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者 class),不会因为之前已经获取过还没释放而阻塞。

Java 中 ReentrantLock 和 synchronized 都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

可重入锁原理:可重入锁的原理是在锁内部维护一个线程标示,用来标示该锁目前被哪个线程占用,然后关联一个计数器。一开始计数器值为 0,说明该锁没有被任何线程占用。当一个线程获取了该锁时,计数器的值会变成 1,这时其他线程再来获取该锁时会发现锁的所有者不是自己而被阻塞挂起。

但是当获取了该锁的线程再次获取锁时发现锁拥有者是自己,就会把计数器值加+1, 当释放锁后计数器值-1。当计数器值为 0 时,锁里面的线程标示被重置为 null,这时候被阻塞的线程会被唤醒来竞争获取该锁。

3. 可重入锁与非可重入性锁

Java 中 ReentrantLock 和 synchronized 都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

为了解释可重入锁与非可重入性锁的区别与联系,我们拿可重入锁 ReentrantLock 和 非重入锁 NonReentrantLock 进行简单的分析对比。

相同点: ReentrantLock 和 NonReentrantLock 都继承父类 AQS,其父类 AQS 中维护了一个同步状态 status 来计数重入次数,status 初始值为 0。

不同点:当线程尝试获取锁时,可重入锁先尝试获取并更新 status 值,如果 status == 0 表示没有其他线程在执行同步代码,则把 status 置为 1,当前线程开始执行。

如果 status != 0,则判断当前线程是否是获取到这个锁的线程,如果是的话执行 status+1,且当前线程可以再次获取锁。

而非可重入锁是直接去获取并尝试更新当前 status 的值,如果 status != 0 的话会导致其获取锁失败,当前线程阻塞,导致死锁发生。

4. 什么情况下使用可重入锁

我们先来看看如下代码:同步方法 helloB 方法调用了同步方法 helloA。

public class DemoTest{public synchronized void helloA(){System.out.println("helloA");}public synchronized void helloB(){System.out.println("helloB");helloA();}
}

在如上代码中,调用 helloB 方法前会先获取内置锁,然后打印输出。之后调用 helloA 方法,在调用前会先去获取内置锁,如果内置锁不是可重入的,那么调用线程将会一直被阻塞。

因此,对于同步方法内部调用另外一个同步方法的情况下,一定要使用可重入锁,不然会导致死锁的发生。

5. synchronized 验证锁的可重入性

为了更好的理解 synchronized 验证锁的可重入性,我们来设计一个简单的场景。

场景设计

  • 创建一个类,该类中有两个方法,helloA 方法和 helloB 方法;
  • 将两个方法内部的逻辑进行 synchronized 同步;
  • helloA 方法内部调用 helloB 方法,营造可重入锁的场景;
  • main 方法创建线程,调用 helloA 方法;
  • 观察结果,看是否可以成功进行调用。

实例

public class DemoTest {public static void main(String[] args) {new Thread(new SynchronizedTest()). start();}
}
class SynchronizedTest implements Runnable {private final Object obj = new Object();public void helloA() { //方法1,调用方法2synchronized (obj) {System.out.println(Thread.currentThread().getName() + " helloA()");helloB();}}public void helloB() {synchronized (obj) {System.out.println(Thread.currentThread().getName() + " helloB()");}}@Overridepublic void run() {helloA(); //调用helloA方法}
}

结果验证

Thread-0 helloA()
Thread-0 helloB()

结果解析:如果同一线程,锁不可重入的话,helloB 需要等待 helloA 释放 obj 锁,如此一来,helloB 无法进行锁的获取,最终造成无限等待,无法正常执行。此处说明了 synchronized 关键字的可重入性,因此能够正常进行两个方法的执行。

6. ReentrantLock 验证锁的可重入性

相同的场景,对代码进行如下改造,将 synchronized 同步代码块修改成 lock 接口同步,我们看代码实例如下:

public class DemoTest {public static void main(String[] args) {new Thread(new SynchronizedTest()). start();}
}
class SynchronizedTest implements Runnable {private final Lock lock = new ReentrantLock();public void helloA() { //方法1,调用方法2lock.lock();try {System.out.println(Thread.currentThread().getName() + " helloA()");helloB();} finally {lock.unlock();}}public void helloB() {lock.lock();try {System.out.println(Thread.currentThread().getName() + " helloB()");} finally {lock.unlock();}}@Overridepublic void run() {helloA();}
}

结果验证

Thread-0 helloA()
Thread-0 helloB()

结果解析:ReentrantLock 一样是可重入锁,试验成功。

7. 小结

锁的可重入性这一概念对于并发编程非常重要,对于本节内容需要深入的理解并掌握。我们之前已经学习过了 synchronized 关键字和 ReentrantLock 锁,此处知识用两者进行了可重入的验证。

本节关键点在于可重入性的意义所在,需要结合实例进行更加细致的理解和掌握。

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

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

相关文章

抽象方法,抽象类,接口的语法以及应用场景

目录 抽象类和接口抽象类接口引用类型数组 抽象类和接口 抽象类 设计准则: 讲派生类中共有的属性和行为,抽到超类----抽共性 若派生类的行为/代码都一样,设置为普通方法。如果不一样则设置为抽象方法。 什么是抽象方法呢? 抽象方…

产品推荐| 立錡低耗电器件:线性稳压器、Buck 和 Boost 转换器

想让电池用得更久、利用好它的每一份电力?低静态电流的电源转换器是你的必然选择。立錡深谙电源管理之道,为你备好了低耗电的各种产品,其中包括低压差线性稳压器、Buck 转换器和 Boost 转换器,最低消耗仅有 360nA,是无…

猎人维修大师免狗版

技术文档摘要 标题: 多功能维修工具集合概述 摘要: 本文档提供了一组多功能维修工具的概述,这些工具旨在为专业技术人员提供便利,以执行设备维修和软件解锁等任务。文档列出了各个工具的主要功能和应用场景。 关键词&#xff1…

探索绿色消费新纪元:消费增值模式的崛起与未来

各位朋友,大家好!我是吴军,来自一家在软件开发领域内广受赞誉的知名企业,担任产品经理一职。今天,我怀着无比激动的心情,与大家分享一种正在全球范围内掀起革新浪潮的新型商业模式——消费增值模式。 近年来…

2023 China Collegiate Programming Contest (CCPC) Guilin (VP桂林 补题)

2023 China Collegiate Programming Contest (CCPC) Guilin (VP桂林 & 补题) 文章目录 2023 China Collegiate Programming Contest (CCPC) Guilin (VP桂林 & 补题)写在前面G. Hard Brackets Problem (签到题)M. Flipping Cards (第二个签到题)K. Randias Permutation …

java项目总结4

目录 1.正则表达式 2.爬虫 3.时间 4.包装类 5.工具类之Arrays 6.Lambda 1.正则表达式 用于验证字符串是否满足自己所需要的规则。方法:matches 注意:\在Java中有特殊涵义,是将其它的意思本来化,假设"是用来引…

【python基础】—入门函数print()的参数解析及使用场景

文章目录 一、print()函数二、区隔符—sep三、结束符号—end四、内容写入文件—file五、缓冲输出设置—flush 一、print()函数 功能 print()函数就是把一个或多个对象转换为其文本表达式形式,然后发送给标准输出流或者类似的文件流。 语法 print(value, …, sep’ …

逆向分析之电脑端如何调试一些只能手机端浏览器才可以打开的网站

手机端浏览器的指纹和电脑端浏览器的指纹是不同的,这样只在手机端浏览器运行的网站则可以检测网站是否满足手机端浏览器指纹的要求,不满足则可以进行一些反爬措施。 例如一些公众号,其实就是使用手机端浏览器打开的H5网站,就可以进行手机端浏览器指纹检测。 这里只是讲解下…

「AI绘画Stable Diffusion 零基础入门必看」ControlNet控制网:轻松控制你的AI绘画出图效果详解

大家好,我是灵魂画师向阳 前言 AI绘画Stable Diffusion 在 ControlNet 出现之前,基于扩散模型的 AI 绘画是极难控制的,因为扩散的过程充满了随机性。 如果只是纯粹自娱自乐,这种随机性并不会带来多大困扰;但在产业化…

安全身份和访问管理:云服务器的守护者

安全身份和访问管理:云服务器的守护者 在云计算时代,身份和访问管理(Identity and Access Management, IAM)是确保云服务器安全的关键组成部分。IAM涉及到控制谁可以访问云资源、他们可以执行什么操作以及如何确保这些访问是安全…

如何高效学习(一)

什么是学习?学习的本质是什么?如何学习?如何更加高效的学习 以下内容均为观看B站UP主(硬核学长2077)所做总结和自我分析 一、自我介绍 ​ 我,一个二三线城市小小程序员,在高中学习就很一般,但当时并没有特…

《框架封装 · Redis 事件监听》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

Object方法重写

这篇文章大家随意看看就好,只是一点理解的东西,当然你也可以认真调查。 我们需要知道Obecj是java中的一个类,是所有类的父类,即超类。对,超级赛亚人的那个超。 我们需要关注其中的equals、tostring这两个方法。 例如&…

电脑管理软件是什么?电脑管理软件能做什么?

电脑管理软件是一种专门设计用于管理和优化计算机系统的软件工具。它涵盖了多个方面,从系统维护、资源分配到安全防护,都有着广泛的应用。以下是对电脑管理软件及其功能的详细解析: 一、电脑管理软件的定义 电脑管理软件是一种管理类型的软…

stm32毫秒ms延时,HAL_Delay()

STM32的HAL库确实提供了毫秒级的延时函数,即HAL_Delay()函数。这个函数使用SysTick定时器来实现延时,并且可以配置为微秒级的延时。 //stm32l4xx_hal.c /*** brief This function provides minimum delay (in milliseconds) based* on variable i…

自动化发布:Conda包依赖的持续集成之旅

自动化发布:Conda包依赖的持续集成之旅 引言 在现代软件开发中,持续集成和持续部署(CI/CD)是提高开发效率和软件质量的关键实践。Conda作为Python和其他科学计算语言的包管理器,支持通过自动化流程发布包依赖。本文将…

现在一个表有自增字段id,姓名,年龄,手机号等等数据,什么场景下可以用到Supplier,Function,Consumer,Predicate等函数式接口

在处理具有自增字段id&#xff0c;姓名&#xff0c;年龄&#xff0c;手机号等字段的数据表时&#xff0c;Supplier、Function、Consumer和Predicate等函数式接口可以应用于多种场景。下面是一些具体的应用场景和示例&#xff1a; Supplier Supplier<T>接口用于生成或提…

宿州降本 提质 增效 数据采集监控平台提高生产自动化水平

在当今竞争激烈的市场环境中&#xff0c;企业追求降本、提质、增效已成为发展的关键。而我们的[数据采集监控平台名称]数据采集监控平台&#xff0c;正是助力企业实现这一目标的强大工具。 LP-SCADA数据采集监控平台是工业4.0中主要的数据采集系统之一&#xff0c;主要针对产线…

SimLab 流体网格建模工具详解 Part2: Mesh

SimLab的建模功能 SimLab中和流体网格建模相关的功能主要集成在 Geometry 和 Mesh 两个标签中。在上期文章中&#xff0c;我们详细介绍了Geometry标签中的内容&#xff0c;本期文章我们将分享继续分享 Mesh 标签的内容&#xff0c;一起来看看吧。 Mesh 标签 标签下的工具生成网…

C++八股(一)

一、new和malloc ⭐ new是C++的关键字,用于动态分配内存并创建对象。它可以根据类型自动计算所需内存空间,并调用对象的构造函数进行初始化。在使用new分配内存后,需要使用delete来释放这些内存空间,以防止内存泄漏。malloc是C语言的库函数,用于动态分配一块指定大小的内存…