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

多线程锁机制面试

目录

乐观锁的底层原理

ReentrantLock的实现原理

读写锁 ReentrantReadWriteLock

synchronized 底层原理

Lock和synchronized的区别


乐观锁的底层原理
  1. 版本号机制 在数据库表中添加一个版本号字段(如 version),每次更新数据时都会将版本号加1。 当尝试更新数据时,会检查当前版本号是否与开始操作时获取的版本号一致。 如果版本号一致,则更新成功;如果不一致,则表示数据已被其他事务修改,更新失败。

  2. 时间戳机制 类似于版本号机制,但在表中使用时间戳字段来记录数据的最后修改时间。 更新数据时,同样需要检查当前时间戳是否与开始操作时获取的时间戳一致。 如果时间戳一致,则更新成功;如果不一致,则表示数据已被其他事务修改,更新失败。

  3. CAS 操作 CAS (Compare and Swap) 是一种无锁算法,用于原子地更新一个变量的值。 在多线程环境中,CAS 操作可以用来实现乐观锁。 CAS 操作通常由 CPU 提供硬件级别的支持,也可以通过软件模拟实现。

乐观锁的优点 减少了锁的竞争,提高了系统的吞吐量。 适用于读多写少的场景。

乐观锁的缺点 如果数据更新频繁,可能导致更新失败的次数增多,从而降低性能。 需要额外的字段存储版本号或时间戳

ReentrantLock的实现原理

ReentrantLock 是 Java 并发包(java.util.concurrent.locks)中提供的一个可重入互斥锁,它提供了比 synchronized 关键字更灵活的锁机制。其核心实现原理基于 AbstractQueuedSynchronizer(AQS)框架

实现原理: 可重入性:ReentrantLock 允许同一个线程多次获取锁而不被阻塞,通过内部维护一个计数器来记录锁被当前线程获取的次数。每次成功获取锁,计数器加1;每次释放锁,计数器减1,当计数器为0时,其他线程才能获取到锁。

公平性与非公平性: 公平锁(FairSync):按照请求锁的顺序来分配锁,先来的线程优先获取锁。实现这一点需要维持一个先进先出(FIFO)的等待队列,线程在尝试获取锁失败后会加入队列末尾等待。

非公平锁(NonfairSync):允许插队,即当前线程尝试获取锁时,即使有其他线程已经在等待队列中,只要锁当前是空闲的,就允许立即获取。这可以减少线程的上下文切换,提高吞吐量,但可能牺牲公平性。

自旋锁与AQS:ReentrantLock 在尝试获取锁时可能会使用自旋策略,即线程在无法立即获取锁时不是立即挂起而是循环等待一段时间,这尤其适用于锁持有时间很短的场景。AQS通过维护一个双向链表来管理等待线程,链表中的每个节点代表一个等待状态的线程,通过CAS操作来保证状态更新的原子性。

条件变量:ReentrantLock 还支持条件变量(Condition),允许线程在满足特定条件前等待,其他线程可以唤醒这些等待的线程。每个条件变量都有自己的等待队列。 锁的释放:释放锁时,不仅会减少锁的持有计数,还会唤醒等待队列中的下一个线程(如果有的话),这通常通过LockSupport类的park/unpark方法实现。

读写锁 ReentrantReadWriteLock
  1. 读写锁的基本概念 读锁: 多个读操作可以同时进行,不会互相阻塞。

  2. 写锁: 写操作是独占的,在写操作进行时不允许任何读操作。

  3. ReentrantReadWriteLock 的特点 可重入性: 同一个线程可以多次获取同一个锁。 公平性: 可以选择公平锁或非公平锁。 读写分离: 读操作和写操作分别使用不同的锁。

  4. ReentrantReadWriteLock 的使用 创建锁: 通过 ReentrantReadWriteLock 类创建读写锁实例。 获取锁: 通过 readLock() 和 writeLock() 方法分别获取读锁和写锁。 锁定与解锁: 使用 lock() 和 unlock() 方法来锁定和解锁。

synchronized 底层原理

synchronized 关键字在 Java 中用于实现线程间的互斥和同步,它基于 JVM 层面的监视器锁(Monitor)机制来实现。以下是 synchronized 的底层原理概述:

1.对象头(Object Header): Java 对象在内存中有一个对象头,其中包含了对象的元数据,如哈希码、GC代信息以及锁信息。 锁信息存储在一个称为 Monitor 的结构中,这个结构是 JVM 提供的,用来实现同步。

Monitor 监视器: 当一个线程试图进入一个由 synchronized 修饰的方法或代码块时,它会尝试获取该对象的 Monitor 锁。 如果锁是可用的,线程将进入临界区,并且锁状态会变为“已锁定”。 如果锁已经被其他线程持有,请求锁的线程将被阻塞,直到锁被释放。

字节码指令: 在字节码级别,synchronized 关键字会被转换为 monitorenter 和 monitorexit 指令。 monitorenter 指令会在执行前尝试获取锁,而 monitorexit 指令则在执行后释放锁。 这些指令操作的是对象头中的 Monitor 结构。

锁升级: 为了提高性能,JVM 实现了锁的升级机制。初始时,锁可能处于无锁状态,然后根据竞争情况升级为偏向锁、轻量级锁或重量级锁。 偏向锁适用于没有多个线程竞争的情况,轻量级锁使用 CAS 操作尝试获取锁,而重量级锁则涉及到操作系统级别的线程阻塞和唤醒。 即时编译优化: HotSpot VM 会对 synchronized 块进行优化,例如,如果检测到锁没有竞争,它可以避免使用重量级锁,从而减少上下文切换和线程调度的开销。

Lock和synchronized的区别
  1. 语法糖 vs 显式控制 synchronized: 是 Java 的关键字,提供了语法级别的支持,用于声明同步代码块或同步方法。 Lock: 是 java.util.concurrent.locks 包中的一个接口,需要显式地获取和释放锁。

  2. 锁的获取与释放 synchronized: 在进入同步代码块或方法时自动获取锁,并在退出时自动释放锁。 Lock: 需要显式地调用 lock() 方法获取锁,并且必须在不再需要锁时显式地调用 unlock() 方法释放锁。为了防止因异常而导致锁未被释放,通常将 unlock() 方法放在 finally 块中。

  3. 可重入性 synchronized: 具有可重入性,允许同一个线程多次获取同一个锁。 Lock: 也支持可重入性,但需要通过 ReentrantLock 类实现。

  4. 公平性 synchronized: 默认是非公平锁。 Lock: 可以通过 ReentrantLock 类的构造函数指定锁是否公平。

  5. 中断响应 synchronized: 无法响应中断请求,即使线程被中断,仍然持有锁。 Lock: 支持中断请求,可以通过 tryLock 方法尝试获取锁,并且可以监听中断信号。

  6. 锁状态的检查 synchronized: 无法检查锁是否被当前线程持有。 Lock: 可以通过 isHeldByCurrentThread() 方法检查当前线程是否持有锁。

  7. 等待/通知机制 synchronized: 直接使用 wait() 和 notify() 方法来实现线程间的等待/通知。 Lock: 通过 Condition 接口实现等待/通知机制,需要与 Lock 结合使用。

  8. 异常处理 synchronized: 在发生异常时会自动释放锁,因此不会出现死锁。 Lock: 发生异常时,如果没有显式地释放锁,可能会导致死锁。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 素数与最大公约数GCD:
  • 基于MVC模式的红色革命文物征集管理系统的设计与实现--论文pf
  • 技术速递|Python in Visual Studio Code 2024年8月发布
  • Node.js版本管理工具之NVM
  • C#关于多线程的线程问题
  • Vue:从入门到放弃
  • 智慧水务项目(七)vscode 远程连接ubuntu 20.04 服务器,调试pyscada,踩坑多多
  • 回归预测|基于鲸鱼优化支持向量机结合Adaboost集成的数据回归预测Matlab程序 多特征输入单输出 效果非常不错!WOA-SVM-Adaboost
  • 探索AAA系统:网络安全与访问控制的核心机制
  • 中英双语介绍金融经济中的鹰派 (Hawkish)和鸽派 (Dovish)
  • 借助Aapose.Cells 使用 C# 在 Excel 中读取、添加和编辑线程注释
  • 从零开始学数据结构系列之第四章《什么是关键路径》
  • windows hook之进程防杀(任务管理器)
  • Python爬虫技术与K-means算法的计算机类招聘信息获取与数据分析
  • 小米便签——ui包详细解读
  • 网络传输文件的问题
  • 《剑指offer》分解让复杂问题更简单
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • co模块的前端实现
  • CSS 三角实现
  • golang中接口赋值与方法集
  • Hibernate【inverse和cascade属性】知识要点
  • Linux快速复制或删除大量小文件
  • React+TypeScript入门
  • Sequelize 中文文档 v4 - Getting started - 入门
  • Vim Clutch | 面向脚踏板编程……
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • WebSocket使用
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 详解移动APP与web APP的区别
  • 阿里云重庆大学大数据训练营落地分享
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​configparser --- 配置文件解析器​
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #includecmath
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • (19)夹钳(用于送货)
  • (2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干
  • (7)svelte 教程: Props(属性)
  • (笔试题)合法字符串
  • (二)构建dubbo分布式平台-平台功能导图
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (回溯) LeetCode 131. 分割回文串
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (五)关系数据库标准语言SQL
  • (一) storm的集群安装与配置
  • (一)基于IDEA的JAVA基础12
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法