Java 并发编程面试题——重入锁 ReentrantLock

目录

  • 1.ReentrantLock 是什么?
  • 2.✨什么是重入锁?ReentrantLock 是如何实现可重入特征的?
  • 3.公平锁和非公平锁有什么区别?ReentrantLock 分别是如何实现的?
  • 4.✨ReentrantLock 的实现原理是什么?
  • 5.为什么 ReentrantLock 默认使用非公平锁?
  • 6.✨synchronized 和 ReentrantLock 有什么区别?
  • 7.将 ReentrantLock 放在方法内部声明会有什么问题?为什么不能放在方法里面?
  • 8.可中断锁和不可中断锁有什么区别?

在了解 ReentrantLock 之前可以先查看这篇文章 Java 并发编程面试题——Lock 与 AbstractQueuedSynchronizer (AQS)

1.ReentrantLock 是什么?

(1)ReentrantLock 实现了 Lock 接口,是一个可重入且独占式的锁,和 synchronized 关键字类似。不过,ReentrantLock 更灵活、更强大,增加了轮询、超时、中断、公平锁和非公平锁等高级功能。

public class ReentrantLock implements Lock, Serializable {
	//...
}

(2)ReentrantLock 里面有一个内部类 Sync,Sync 继承 AQS (AbstractQueuedSynchronizer),添加锁和释放锁的大部分操作实际上都是在 Sync 中实现的。Sync 有公平锁 FairSync 和非公平锁 NonfairSync 两个子类。

在这里插入图片描述

(3)ReentrantLock 默认使用非公平锁,也可以通过构造器来显示的指定使用公平锁。

// fair 为 true 时为公平锁,false 时为非公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住。

2.✨什么是重入锁?ReentrantLock 是如何实现可重入特征的?

(1)重入锁是指支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,ReentrantLock 本身就是重入锁。重入锁具备重入特性,即允许同一个线程多次获取锁,而不会导致线程死锁。这使得编程更加灵活,可避免一些潜在的问题。

(2)可重入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞,该特性的实现需要解决以下两个问题:

  • 线程再次获取锁:锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
  • 锁的最终释放:线程重复 n 次获取了锁,随后在第 n 次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于 0 时表示锁已经成功释放。

(3)ReentrantLock 是通过组合自定义同步器来实现锁的获取与释放,以非公平性(默认的)实现为例,其处理逻辑如下:

  • 线程再次获取锁:通过判断当前线程是否为获取锁的线程来决定获取操作是否成功,如果是获取锁的线程再次请求,则将同步状态值 state 进行加一操作并返回 true,表示获取同步状态成功;
  • 锁的最终释放:成功获取锁的线程再次获取锁,只是增加了同步状态值,这也就要求 ReentrantLock 在释放同步状态时减少同步状态值。如果该锁被获取了 n 次,那么前 (n - 1) 次 tryRelease(int releases) 方法必须返回 false,而只有同步状态完全释放了,才能返回true。可以看到,该方法将同步状态是否为 0 作为最终释放的条件,当同步状态为 0 时,将占有线程设置为 null,并返回 true,表示释放成功;

思考:为什么需要可重入锁?
回答:在多线程编程中,可能会出现线程需要在多个方法中嵌套使用同一把锁的情况。如果没有可重入锁的支持,那么一个线程在持有锁的情况下再次请求这个锁时,就会发生自己阻塞的情况,导致出现死锁。可重入锁的出现则解决了这个问题,它允许线程多次获取同一把锁,确保在未释放锁之前,线程仍然可以正常地访问该锁所保护的资源。

3.公平锁和非公平锁有什么区别?ReentrantLock 分别是如何实现的?

(1)公平锁和非公平锁之间的区别如下:

  • 公平锁 : 锁被释放之后,会严格按照请求线程的顺序来分配锁,即先到先得。性能较差一些,因为公平锁为了保证时间上的绝对顺序,上下文切换更频繁。
  • 非公平锁:锁被释放之后,允许线程通过插队来抢占锁资源,即后申请的线程可能会先获取到锁,是随机或者按照其他优先级排序的。性能更好,但可能会导致某些线程永远无法获取到锁。

(2)ReentrantLock 默认使用非公平锁,其内部使用了 AQS 来实现锁资源的竞争,没有竞争到锁资源的线程会加入到 AQS 的同步队列中,该同步队列是由一个双向链表实现的:

  • ReentrantLock 实现公平锁的方式是线程在竞争锁资源时会判断同步队列中有没有等待线程,如果有就加入到同步队列的尾部进行等待;
  • 非公平锁的实现方式是不管同步队列里面有没有线程等待,它都会先去尝试抢占锁资源,如果抢占不到,才会加入到同步队列的尾部进行等待。

4.✨ReentrantLock 的实现原理是什么?

ReentrantLock 实现了 Lock 接口,是一个可重入且独占式的锁,ReentrantLock 的底层实现有以下几个非常重要的方面:

  • 锁的竞争
    • ReentrantLock 有一个非常重要的内部类 Sync,Sync 继承 AQS,添加锁和释放锁的大部分操作实际上都是在 Sync 中实现的。
    • AQS 采用了一个 int 类型的互斥变量 state 用来记录锁竞争的一个状态,0 表示当前没有任何线程竞争锁资源,而大于等于 1 表示已经有线程正在持有锁资源。一个线程来获取锁资源的时候,首先判断 state 是否等于 0,如果是(无锁状态),则把这个 state 更新成 1,表示占用到锁。此时如果多个线程进行同样的操作,会造成线程安全问题。AQS 采用了 CAS 机制来保证 state 的原子性。
    • 对于没有竞争到锁的线程,它们会被存储到 AQS 的同步队列中,该同步队列的底层是通过双向链表来实现的。当锁被释放之后,会从 AQS 队列里面的头部唤醒下一个等待锁的线程。具体过程如下图所示:
      在这里插入图片描述
  • 锁的可重入:可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁,其余具体实现过程见上面的分析;
  • 锁的公平性和非公平性:Sync 有公平锁 FairSync 和非公平锁 NonfairSync 两个子类,其余具体实现过程见上面的分析;

5.为什么 ReentrantLock 默认使用非公平锁?

(1)ReentrantLock 默认使用非公平锁是为了优化性能。非公平锁在获取锁时不会考虑等待队列中其他线程的顺序,而是允许新到来的线程插队获取锁,这在某些情况下可以减少线程切换的开销,提高整体的吞吐量

(2)而公平锁则会遵循先进先出的原则,保证所有线程按照请求的顺序获取锁,这会增加线程切换的开销,但可以避免饥饿的发生。由于大多数情况下公平性不是一个强制要求,因此非公平锁被认为是默认的更合理选择。不过,如果有特定的需求,ReentrantLock 也提供了可以配置的公平锁选项。

6.✨synchronized 和 ReentrantLock 有什么区别?

(1)synchronized 和 ReentrantLock 都是用于实现线程同步的机制,它们的主要区别在于以下几个方面:

  • 本质:synchronized 是⼀个关键字,ReentrantLock 是⼀个
  • 加锁方式:synchronized 会自动的加锁与释放锁,ReentrantLock 则需要通过 lock() 方法与 unlock() 方法手动加锁与释放锁;
  • 公平性:ReentrantLock 默认情况是非公平的,但可以通过 ReentrantLock 类的 ReentrantLock(boolean fair) 构造方法来制定是否是公平的;而 synchronized 只能是非公平锁,即无法保证等待时间最长的线程先获得锁。
  • 锁性质:synchronized 是 JVM 层面的锁;ReentrantLock 是 API 层面的锁;
  • 中断支持:ReentrantLock 提供了可以响应中断的获取锁的方式,而 synchronized 没有中断的语义。
  • 等待可中断:ReentrantLock 提供了一个 tryLock(long time, TimeUnit unit) 方法,该方法可以尝试获取锁一段时间,在指定时间内获取不到锁会返回 false,从而避免了线程一直阻塞的情况。
  • 锁状态保存:synchronized 锁的是对象,锁信息保存在对象头中;ReentrantLock 通过代码中 int 类型的 state 变量来标识锁的状态;
  • 条件变量:ReentrantLock 可以配合 Condition 接口实现更灵活的线程通信和同步。
  • 性能:在高并发情况下,ReentrantLock 通常比 synchronized 性能更好。

(2)综上所述,虽然 synchronized 简单易用,但是在一些复杂的场景下可能无法满足需求,此时可以使用 ReentrantLock 来实现更灵活的线程同步。但需要注意的是,由于 ReentrantLock 比 synchronized 更加复杂,使用不当可能会导致死锁等问题,因此在使用 ReentrantLock 时需要格外小心。

相关知识点:
Java 并发编程面试题——synchronized 与 volatile
Java 并发编程面试题——Condition 接口

7.将 ReentrantLock 放在方法内部声明会有什么问题?为什么不能放在方法里面?

(1)ReentrantLock 是 Java 提供的一个可重入锁实现,用于在多线程环境中提供更灵活的同步控制。通常情况下,应该将 ReentrantLock 的声明放在类的成员变量位置,而不是放在方法内部。如果将 ReentrantLock 放在方法内部声明,会导致以下问题:

  • 互斥性失效:每次方法调用都会创建一个新的 ReentrantLock 对象,这意味着每个线程都会获取到一个独立的锁。因此,无法实现多个线程之间的互斥访问,即使是同一个线程多次调用同一个方法,也无法保证其互斥性。
  • 数据共享困难:将 ReentrantLock 放在方法内部,会导致每个线程拥有自己的锁对象。这会造成在线程间共享数据时的困扰,因为多个线程无法共享同一个锁对象来保护共享数据的访问。如果不同的线程持有不同的锁对象,那么锁的语义将失效,无法保证数据的一致性和正确性。
  • 锁无法释放:当将 ReentrantLock 放在方法内部声明时,每个线程都会创建一个独立的锁对象,并在其方法执行完毕后释放锁。这意味着即使其他线程仍然需要访问该方法所保护的共享资源,它们无法获取到被锁保护的权限,从而造成了资源的长时间阻塞和不必要的等待。

(2)综上所述,不推荐将 ReentrantLock 放在方法内部声明。相反,应该将 ReentrantLock 的声明放在类的成员变量位置,使得所有线程都共享同一个锁对象,并确保互斥性、数据一致性和正确的资源释放

8.可中断锁和不可中断锁有什么区别?

  • 可中断锁 :获取锁的过程中可以被中断,不需要一直等到获取锁之后才能进行其他逻辑处理。ReentrantLock 就属于是可中断锁。
  • 不可中断锁 :一旦线程申请了锁,就只能等到拿到锁以后才能进行其他的逻辑处理。 synchronized 就属于是不可中断的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sui与SUMM3R推出NFT增长计划,毕业即提供5万美元资助

为了寻找并支持专注于NFT的高质量初创企业,我们自豪地宣布在Sui上推出NFT增长计划SUMM3R。这是一个为种子期和初创期公司提供的综合计划,该加速器将利用Sui独特的NFT技术,为它们提供无与伦比的知识、连接和资源,以建立可持续的业务…

k8s 部署mqtt —— 筑梦之路

mqtt是干嘛的,网上有很多资料,这里就不再赘述。 --- apiVersion: apps/v1 kind: Deployment metadata:labels:app: mqttname: mqttnamespace: default spec:replicas: 1selector:matchLabels:app: mqttstrategy:rollingUpdate:maxSurge: 25%maxUnavaila…

Leetcode_2:两数相加

题目描述: 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff…

远程运维如何更高效的远程管理?向日葵的这几项功能会帮到你

具备一定规模的企业,其IT运维需求普遍会面临设备数量众多、难以统一高效管理、始终存在安全敞口等问题,尤其是针对分部广泛的无人值守设备时,更是如此。 举一个简单的例子,一台位于商圈的无人值守可互动广告机设备,所…

第18章Swing程序设计

Swing程序设计 Swing用于开发桌面窗体程序用于JDK的第二代GUI框架,其功能比JDK第一代GUI框架AWT更为强大,性能更加优良。但因为Swing技术推出时间太早,七性能,开发效率等不及一些其他的留下技术,所以目前市场大多数桌面…

Windows系统下本地MQTT服务器搭建(保姆级教程)

Windows系统下本地MQTT服务器搭建 1.下载并安装emqx服务器 1. 访问Eqmx官网 2. 选中合适的MQTT服务器版本 由于我们使用的是本地部署MQTT服务器,而且只使用基础功能的MQTT服务器功能,所以选中“大规模分布式MQTT消息服务器”即可,如下如图…

【Copilot】登录报错 Extension activation failed: “No auth flow succeeded.“(VSCode)

问题描述 Visual Studio Code 登录 GitHub Copilot 插件报错。 在浏览器中成功授权 GitHub 账户,返回 VSCode 后仍然报错。 [ERROR] [default] [2023-11-06T12:34:56.185Z] Extension activation failed: "No auth flow succeeded."原因分析 网络环境问…

mongo实际业务场景实战

业务场景 有四个业务信息,分别是适用部门、适用岗位、适用职级、适用专业。 1.适用部门有三个层级类似D001表示一级部门、D001002表示二级部门、D001002001表示三级部门,ALL表示所有部门。 2.适用岗位有岗位A、岗位B、ALL等,ALL表示适用所有岗位。 3.适用职级有M-1,M-2、AL…

csharp写一个招聘信息采集的程序

csharp爬虫是一种用于自动化抓取网页内容的程序。它可以通过模拟人类浏览器的行为,自动访问网站并抓取所需的数据。csharp爬虫可以用于各种场景,例如数据挖掘、搜索引擎优化、竞争情报等。但是,使用csharp爬虫需要注意一些问题,例…

再获5G RedCap能力认证!宏电5G RedCap工业智能网关通过中国联通5G物联网OPENLAB开放实验室测试验证

​近日,中国联通5G物联网OPENLAB开放实验室携手宏电股份完成5G RedCap工业智能网关端到端的测试验证,并颁发OPENLAB实验室面向RedCap终端的认证证书,为RedCap产业规模推广、全行业赋能打下坚实基础。 中国联通5G物联网OPENLAB开放实验室是中国…

【kylin】使用nmtui软件配置网桥

文章目录 一、什么是网桥二、如何配置网桥域名解析失败 一、什么是网桥 网桥也叫桥接器,是连接两个局域网的一种存储/转发设备,它能将一个大的LAN分割为多个网段,或将两个以上的LAN互联为一个逻辑LAN,使LAN上的所有用户都可访问服…

Selenium切换窗口句柄及调用Chrome浏览器

一. 调用Chrome浏览器 首先,假设通过Firefox()浏览器定向爬取首页导航栏信息,审查元素代码如下图所示,在div class="menu"路径的ul、li、a下,同时可以定位ul class="clearfix"。 # coding=utf-8 import os from selenium import webdriver #…

react antd message多条数据展示样式

最终效果: 前言: 平时我们经常用到message来做一些错误提示,最常见的就是单行提示。如下图: 实现代码: message.error(This is an error message)多行动态message实现 参考文献:antd message 链接地址&a…

java版本转换小工具

工作之余写了一个转换小工具,具有以下功能: 时间戳转换Base64编码/解码URL编码/解码JSON格式化 时间戳转换 package org.binbin.container.panel;import javax.swing.*; import java.awt.*; import java.text.DateFormat; import java.text.SimpleDat…

【verilog】verilog语法刷题知识点总结

verilog语法刷题知识点总结 1.状态机2.任务和函数的区别3.case,casez和casex4.随机数产生关键字5.运算符优先级6.特殊运算符(1)移位运算符(2)等式运算符(3)动态位宽截取运算符 7.testbench知识点 1.状态机 (1)三段式状态机的组成&#xff1a…

Vue.Draggable 踩坑:add 事件与 change 事件中 newIndex 字段不同之谜

背景 最近在弄自定义表单,需要拖动组件进行表单设计,所以用到了 Vue.Draggable(中文文档)。Vue.Draggable 是一款基于 Sortable.js 实现的 vue 拖拽插件,文档挺简单的,用起来也方便,但没想到接下来给我遇到了灵异事件……

钉钉API与集简云无代码开发连接:电商平台与营销系统的自动化集成

连接科技与能源:钉钉API与集简云的一次集成尝试 在数字化时代,许多公司面临着如何将传统的工作方式转变为更智能、高效的挑战。某能源科技有限公司也不例外,他们是一家专注于能源科技领域的公司,产品包括节能灯具、光伏逆变器、电…

山西电力市场日前价格预测【2023-11-11】

日前价格预测 ​ 预测说明: 如上图所示,预测明日(2023-11-11)山西电力市场全天平均日前电价为311.30元/MWh。其中,最高日前电价为417.73元/MWh,预计出现在08: 00。最低日前电价为151.48元/MWh&#xff0c…

媒体转码软件Media Encoder 2024 mac中文版功能介绍

Media Encoder 2024 mac是一款媒体转码软件,它可以将视频从一种格式转码为另一种格式,支持H.265、HDR10等多种编码格式,同时优化了视频质量,提高了编码速度。此外,Media Encoder 2024还支持收录、创建代理和输出各种格…

Spark 读取ES采坑系列

目录 一、使用的插件 二、ES集群和Elasticsearch-hadoop版本问题 三、Elasticsearch-hadoop 和Scala版本以及Spark版本&#xff08;版本不匹配会有各种异常信息 一、使用的插件 <dependency><groupId>org.elasticsearch</groupId><artifactId>elas…