【Java】面试题 并发安全 (2)
文章目录
- 可重入锁(ReentrantLock)知识总结
- 1. 可重入锁概念与特点
- 2. 基本语法与使用注意事项
- 3. 底层实现原理
- 4. 面试回答要点
- synchronized与lock的区别
- 死锁相关面试题讲解
- 死锁产生的四个条件
- ConcurrentHashMap
- 2. JDK1.7的ConcurrentHashMap
- 结构
- 添加数据逻辑
- 存在问题
- 3. JDK1.8的ConcurrentHashMap
- 结构优化
- 保证线程安全方式
- 4. 对比总结
- 底层数据结构
- 锁的方式
- 导致并发程序出现问题的根本原因
可重入锁(ReentrantLock)知识总结
1. 可重入锁概念与特点
- 概念:ReentrantLock是可重入锁,同一线程可多次获取该锁。
- 特点
- 可中断:与synchronized不同,ReentrantLock可在获取锁过程中被中断。
- 可设置超时时间:获取锁时可设置超时时间,超时未获取到锁可放弃,避免无限等待。
- 支持公平锁和非公平锁:默认是非公平锁,也可通过构造函数设置为公平锁。公平锁按等待顺序获取锁,非公平锁允许插队,提高性能但可能导致某些线程长时间等待。
- 支持多个条件变量:类似synchronized中的wait/notify方法,可创建多个条件变量控制线程等待和唤醒,更灵活。

2. 基本语法与使用注意事项
- 语法
- 创建ReentrantLock对象。
- 在try块中调用lock方法获取锁。
- 在finally块中调用unlock方法释放锁,确保锁一定释放,避免死锁。
- 注意事项:必须在finally块中释放锁,防止异常导致锁未释放引发死锁。

3. 底层实现原理
- 基于AQS实现:ReentrantLock底层依赖AbstractQueuedSynchronizer(AQS)实现,AQS维护同步状态和线程等待队列。
- 构造函数与锁类型
- 无参构造函数默认创建非公平锁。
- 带参数构造函数可传入特定参数创建公平锁或非公平锁。
- 工作方式
- 非公平锁获取锁:线程先尝试通过CAS操作修改同步状态state,成功则获取锁并设置当前线程为持有锁线程;失败则进入等待队列,但进入队列前仍会再次尝试获取锁(插队行为),若此时锁可用可直接获取。
- 公平锁获取锁:线程先检查等待队列中是否有前驱节点,有则进入等待队列按先来先得顺序获取锁;无前驱节点则尝试通过CAS操作获取锁。
- 锁释放:释放锁时唤醒等待队列中的线程重新竞争锁。
4. 面试回答要点
- 强调ReentrantLock是可重入锁,同一线程可多次调用lock方法。
- 提及底层主要使用CAS和AQS实现,重点解释AQS工作原理。
- 说明ReentrantLock支持公平锁和非公平锁,无参构造函数默认是非公平锁,可传参设置公平锁。

文章目录
- 可重入锁(ReentrantLock)知识总结
- 1. 可重入锁概念与特点
- 2. 基本语法与使用注意事项
- 3. 底层实现原理
- 4. 面试回答要点
- synchronized与lock的区别
- 死锁相关面试题讲解
- 死锁产生的四个条件
- ConcurrentHashMap
- 2. JDK1.7的ConcurrentHashMap
- 结构
- 添加数据逻辑
- 存在问题
- 3. JDK1.8的ConcurrentHashMap
- 结构优化
- 保证线程安全方式
- 4. 对比总结
- 底层数据结构
- 锁的方式
- 导致并发程序出现问题的根本原因
synchronized与lock的区别

- 面试题引入
- 题目:synchronized与lock有什么区别。
- 回答思路:从语法、功能、性能三个层面回答,重点在功能层面。
- 语法层面区别
- 实现方式:synchronized是关键字,由JVM提供,C++语言实现;lock由JDK提供,用Java语言实现。
- 锁释放机制:使用synchronized时,退出同步块会自动释放锁;lock需调用unlock才能释放锁。
- 功能层面区别
- 相同点:都属于
悲观锁,具备互斥同步和锁重入功能。 - 不同点:lock提供更多功能,如公平锁、可打断、可超时、多条件变量等。
- 公平锁:使用ReentrantLock时,可通过带条件的构造函数传true实现。
- 可打断锁
- 演示代码:创建t1线程,使用
lock.Interruptibly()方法开启可打断锁,若被打断抛异常,否则正常进入锁并释放锁。
- 演示代码:创建t1线程,使用
- 可超时锁
- 演示代码:线程获取锁调用
tryLock方法,成功返回true,失败返回false,可设置超时时间,超时后放弃获取锁或获取成功执行业务。
- 演示代码:线程获取锁调用
- 多条件变量
- 演示代码:创建锁后可多次调用
new Condition声明多个条件变量,线程可按条件等待
- 演示代码:创建锁后可多次调用
- 相同点:都属于
- 适合不同场景的实现及读写锁(功能层面)
- ReentrantLock:与synchronized类似但功能更多。
- 读写锁(ReentrantReadWriteLock):能支撑更高并发量,读操作可不加锁,写操作需加锁,适用于大量读需求的场景。
- 性能层面区别
- 无竞争时:synchronized做了很多优化,如偏向锁、轻量级锁,性能还行。
- 竞争激烈时:lock往往提供更好性能。
死锁相关面试题讲解

- 死锁产生条件及示例演示
- 产生条件:一个线程同时获取多把锁时易发生死锁。
- 示例代码分析:代码中有
object a和object b两个对象,t1线程先获取a锁,在a锁代码块中再获取b锁;t2线程先获取b锁,在b锁代码块中再获取a锁,然后开启两个线程,程序会一直运行,出现死锁。
- JDK工具诊断死锁问题介绍
- 死锁情况:
t1持有a锁等待b锁,t2持有b锁等待a锁,形成死锁。 - 工具介绍:使用
jdk提供的jps和jstack工具诊断死锁。jps可输出当前运行的所有进程状态信息,jstack可查看java进程内线程的堆栈信息。 - 诊断步骤:先使用
jps找到死锁代码的进程id(如24380),再使用jstack加进程id查看日志信息,日志最后会提示死锁,分析t1、t2线程等待和锁住的锁,以及可能出现问题的代码行数,根据提示打开代码分析多把锁导致的死锁问题并修改。
- 死锁情况:

- JDK可视化工具
jconsole检查死锁- 工具介绍:
jdk自带可视化工具jconsole,可用于jvm的内存、线程、类的监控。 - 检查步骤:打开
jconsole,选择本地连接,找到要监控的进程(如24380),选择不安全链接,点击线程选项卡中的检查死锁,可看到t2、t1线程发生死锁及相关日志,根据提示检查对应代码解决死锁问题。
- 工具介绍:
VisualVM检查死锁及总结- 检查流程:找到
VisualVM(安装目录与jconsole相同),双击打开,在本地选择要检查的进程(如24380),切换到进程权限,会提示检查到死锁,点击线程dump获取更多信息,日志中可看到与jstack类似的t1、t2线程等待和锁住锁的信息,根据提示的代码行号到附近查找问题。 - 总结:死锁产生条件是一个线程同时获得多把锁。诊断方法可先使用
jps和jstack,也可使用jconsole或VisualVM检查死锁问题,根据提示分析代码解决死锁。
- 检查流程:找到
死锁产生的四个条件

死锁是指在多道程序环境下,多个进程因竞争资源而造成的一种僵局,若无外力作用,这些进程都将无法向前推进。在操作系统中,死锁产生的条件有以下四个:
- 互斥条件:指进程对所分配到的资源进行排他性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其他进程请求该资源,则请求者只能等待,直至占有该资源的进程释放。
- 请求和保持条件:指进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 不可剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
- 环路等待条件:指在发生死锁时,必然存在一个进程-资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
这四个条件是死锁产生的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。在实际操作系统中,为了避免死锁的发生,通常会采用一些策略来破坏这些条件,例如资源分配策略、死锁检测与恢复机制等。
ConcurrentHashMap
Java 8的 ConcurrentHashMap 是线程安全的哈希表,有如下关键特性:
- 数据结构:采用数组加链表加红黑树结构。初始为数组,链表长度达阈值(8)且桶数超64时,链表转红黑树,提升查找效率。
- 并发控制:摒弃Java 7的分段锁,改用更细粒度的锁机制。使用
CAS操作更新部分内容,对单个桶用synchronized同步,降低锁粒度,提升并发性能。 - 常用方法:
put用于插入键值对;get根据键取值;compute对指定键计算并更新或插入值;forEach遍历键值对。 - 适用场景:适用于高并发、读多写少场景,以及多线程环境下需线程安全哈希表的情况。
2. JDK1.7的ConcurrentHashMap

结构
- 整体结构:采用分段的数组加链表实现。
- Segment数组:不能扩容,每个下标对应另一个可扩容数组,该数组可挂链表或直接存储数据。
添加数据逻辑
- 计算
key哈希值定位Segment数组下标。 - 找到下标后用
ReentrantLock锁住该位置。 - 再次通过哈希值定位哈希表数组位置存储数据。
存在问题
- 性能低:多个
key定位到同一Segment下标时,只有一个线程能操作。 - 数组不能扩容:
Segment数组长度在创建时确定。
3. JDK1.8的ConcurrentHashMap

结构优化
- 放弃
Segment数组,采用与HashMap相同结构,即数组加链表加红黑树。
保证线程安全方式
- 添加新节点:通过
CAS自旋操作保证数据安全。 - 已有链表或红黑树:用
synchronized锁住首节点,锁力度更细,效率更高。
4. 对比总结
底层数据结构
- 1.7:分段的数组加链表。
- 1.8:数组加链表加红黑树。
锁的方式
- 1.7:
Segment数组的分段锁(ReentrantLock),锁住范围大。 - 1.8:添加新节点用
CAS,对链表或红黑树用synchronized锁首节点,性能更好。
导致并发程序出现问题的根本原因
- 并发编程三大特性
- 原子性
- 定义:线程在CPU中的操作不可暂停、中断,要么执行完成,要么不执行。
- 抢票代码示例:有10张票,多线程抢票可能出现超卖或一张票卖给多人,证明代码非原子操作。
- 解决方法:加锁(
synchronized关键字、lock锁)使代码具有原子性。
- 可见性
- 定义:一个线程对共享变量修改后,要让另一个线程可见。
- 代码示例:线程一循环取反共享变量
flag,线程二将其改为true,但线程一可能读不到该修改,循环不退出。 - 解决方法:加锁(性能不高)或在共享变量上使用
volatile。
- 有序性
- 指令重排概念:处理器为提高效率可能打乱代码顺序执行,可能导致问题。
- 代码示例:对共享变量
x和y进行赋值和读取操作,若出现x = 1, y = 0的情况,可能是指令重排序。 - 解决方法:在共享变量上添加
volatile(最好在y上添加)禁止指令重排序。
- 原子性
- 总结
- 回答导致并发程序出现问题的根本原因时,需提及并发编程的三大特性及解决方法。
- 原子性用
sync或lock锁解决;内存可见性推荐用volatile,也可用锁;有序性通过在共享变量上加volatile禁止指令重排序。
相关文章:
【Java】面试题 并发安全 (2)
文章目录 可重入锁(ReentrantLock)知识总结1. 可重入锁概念与特点2. 基本语法与使用注意事项3. 底层实现原理4. 面试回答要点 synchronized与lock的区别死锁相关面试题讲解死锁产生的四个条件ConcurrentHashMap2. JDK1.7的ConcurrentHashMap结构添加数据…...
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
这个包丢失了 启动不了 起因是pom中加入了 <tomcat.version></tomcat.version>版本指定,然后idea自动编译后,包丢了,删除这个配置后再也找不回来, 这个包正常在 <dependency><groupId>org.springframe…...
React 组件的通信方式
在 React 应用开发中,组件之间的通信是构建复杂用户界面和交互逻辑的关键。正确地实现组件通信能够让我们的应用更加灵活和易于维护。以下是几种常见的 React组件通信方式。 一、父子组件通信 1. 通过 props 传递数据(父组件向子组件传递数据࿰…...
WAV文件双轨PCM格式详细说明及C语言解析示例
WAV文件双轨PCM格式详细说明及C语言解析示例 一、WAV文件双轨PCM格式详细说明1. WAV文件基本结构2. PCM编码方式3. 双轨PCM格式详细说明二、C语言解析WAV文件的代码示例代码说明一、WAV文件双轨PCM格式详细说明 WAV文件是一种用于存储未压缩音频数据的文件格式,广泛应用于音频…...
【ES6复习笔记】数值扩展(16)
介绍 在 JavaScript 中,数值扩展提供了一些额外的功能,使得处理数值变得更加方便。本教程将介绍一些常用的数值扩展方法和属性。 1. Number.EPSILON Number.EPSILON 是 JavaScript 表示的最小精度。它的值接近于 2.2204460492503130808472633361816E-…...
百度热力图数据日期如何选择
目录 1、看日历2、看天气 根据研究内容定,一般如果研究城市活力的话,通常会写“非重大节假日,非重大活动,非极端天气等”。南方晴天不多,有小雨或者中雨都可认为没有影响,要不然在南方很难找到完全一周没有…...
Vue.js 高级组件开发:设计模式与实践
Vue.js 高级组件开发:设计模式与实践 引言一、组合式 API 与动态依赖注入1. 基于 provide/inject 的动态依赖2. 动态依赖注入与懒加载 二、动态渲染与自定义渲染函数1. 使用 Render 函数动态生成内容2. 自定义 vnode 操作 三、复杂场景下的动态表单生成与验证四、高…...
《一文读懂卷积网络CNN:原理、模型与应用全解析》
《一文读懂卷积网络CNN:原理、模型与应用全解析》 一、CNN 基本原理大揭秘(一)从人类视觉到 CNN 灵感(二)核心组件详解 二、经典 CNN 模型巡礼(一)LeNet-5:开山鼻祖(二&a…...
MONI后台管理系统-数据敏感字段存储加密
前言: 在我们数据库中,存在很多的敏感数据,如用户表中,存在用户电话、身份证号、邮箱等属于用户的敏感信息,我们通常在存入数据库后,将其进行加密存储,以此来保证数据安全性。 …...
熟悉各类游戏设计模式的用途与限制,如 factory、strategy、mvc、object pool 等
良好的系统分析与设计能力要求开发者熟悉并正确运用各种设计模式来解决特定问题。设计模式是一种针对特定问题的通用解决方案,可提高代码的可复用性、可维护性和可扩展性。以下是对一些常见游戏设计模式的详细分析,包括其用途、限制和代码示例。 一、工厂…...
【RabbitMQ高级篇】消息可靠性问题(1)
目录 1.消息可靠性 1.1.生产者消息确认 1.1.1.修改配置 1.1.2.定义Return回调 1.1.3.定义ConfirmCallback 1.2.消息持久化 1.2.1.交换机持久化 1.2.2.队列持久化 1.2.3.消息持久化 1.3.消费者消息确认 1.3.1.演示none模式 1.3.2.演示auto模式 1.4.消费失败重试机制…...
ASP.NET |日常开发中常见问题归纳讲解
ASP.NET |日常开发中常见问题归纳讲解 前言一、性能问题1.1 数据库访问性能1.2 视图状态(在ASP.NET Web Forms 中) 二、安全问题2.1 SQL 注入2.2 跨站脚本攻击(XSS) 三、状态管理问题3.1 会话状态(Session …...
【【深入浅出TinyRisc-v】】
深入浅出TinyRisc-v 本代码参考于 https://gitee.com/liangkangnan/tinyriscv 自己理解之后又重新写了一遍 tinyriscv.v // 涓嬮潰鏄鏁翠釜top妯″潡鐨勪功鍐? module tinyriscv(input clk ,input rst_n …...
常见的限流算法
常见的限流算法 限流的定义固定窗口算法滑动窗口算法漏桶算法(推荐)令牌桶算法(推荐)限流粒度本地限流(单机限流)分布式限流(多机限流)分布式限流的实现 限流的定义 限流,也称流量控制。是指系统…...
【Leetcode 每日一题】3159. 查询数组中元素的出现位置
问题背景 给你一个整数数组 n u m s nums nums,一个整数数组 q u e r i e s queries queries 和一个整数 x x x。 对于每个查询 q u e r i e s [ i ] queries[i] queries[i],你需要找到 n u m s nums nums 中第 q u e r i e s [ i ] queries[i] q…...
xadmin后台首页增加一个导入数据按钮
xadmin后台首页增加一个导入数据按钮 效果 流程 1、在添加小组件中添加一个html页面 2、写入html代码 3、在urls.py添加导入数据路由 4、在views.py中添加响应函数html代码 <!DOCTYPE html> <html lang...
行为树详解(5)——事件驱动
【分析】 如果行为树的节点很多,那么会存在要经过很多节点才会走到动作节点的情况。显然,性能上不如状态机。 每帧都需要重新遍历一系列节点才会走到动作节点,而实际上很多条件节点在数帧内不会有变化,这是造成性能问题的重要原…...
3.若依前端项目拉取、部署、访问
因为默认RuoYi-Vue是使用的Vue2,所以需要另外去下载vue3来部署。 拉取代码 git clone https://gitee.com/ys-gitee/RuoYi-Vue3.git 安装node才能执行npm相关的命令 执行命令npm install 如果npm install比较慢的话,需要添加上国内镜像 npm install --registrhttp…...
Debian操作系统相对于Ubuntu有什么优势吗?
更高的稳定性:Debian 以其出色的稳定性闻名,得益于严格的软件包测试和发布流程。其稳定版经过长时间测试与验证,确保了系统的高度稳定,更适合对稳定性要求极高的长期运行服务器环境。而 Ubuntu 虽有稳定版本,但更新周期…...
【漏洞复现】CVE-2015-3337 Arbitrary File Reading
漏洞信息 NVD - CVE-2015-3337 Directory traversal vulnerability in Elasticsearch before 1.4.5 and 1.5.x before 1.5.2, when a site plugin is enabled, allows remote attackers to read arbitrary files via unspecified vectors. 在安装了具有“site”功能的插件以…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
