介绍一下你知道的锁

一、核心概念与分类(基础必问)

从不同维度对锁进行分类。

1. 乐观锁 vs 悲观锁(思想层面)

  • 悲观锁 :认为并发冲突一定会发生 ,因此在操作数据前必须先加锁。典型代表是 synchronized 关键字和 ReentrantLock
    • 类比:假设所有人都会修改数据,所以每次进屋(操作数据)都先锁门。
  • 乐观锁 :认为并发冲突不常发生 ,因此先直接操作数据,在提交更新时再判断是否有冲突。通常通过版本号CAS机制 实现。
    • 典型实现 :数据库的 version 字段,Java中的 AtomicInteger(基于CAS)。
    • 类比:假设大家很少冲突,所以先干事,提交前看一眼有没有人动过(检查版本号)。

2. 公平锁 vs 非公平锁(调度策略)

  • 公平锁 :多个线程按照申请锁的顺序 来获取锁,先到先得。ReentrantLock(true)
    • 优点:不会"饿死"。
    • 缺点:吞吐量较低,需要维护一个队列。
  • 非公平锁 :允许"插队",线程尝试获取锁时,如果锁刚好可用,则直接获取,不管队列里是否有等待者。synchronizedReentrantLock()(默认)。
    • 优点:吞吐量高,减少线程切换开销。
    • 缺点:可能导致某些线程长时间"饿死"。

3. 可重入锁 vs 非可重入锁(能否重复进入)

  • 可重入锁(递归锁) :同一个线程在外层方法获取锁后,在进入内层方法时会自动获取该锁(锁的计数器+1)。synchronizedReentrantLock 都是可重入的。
    • 优点:避免死锁,方便递归和嵌套调用。
    • 示例synchronized 方法A调用另一个 synchronized 方法B。
  • 非可重入锁:与之相反,自己锁了自己就不能再进了。

4. 独享锁(排他锁) vs 共享锁(读写锁)

  • 独享锁 :一次只能被一个线程持有。synchronizedReentrantLock 是独享锁。
  • 共享锁 :允许多个线程同时持有。典型代表是 ReentrantReadWriteLock.ReadLock
    • 读写锁ReentrantReadWriteLockReadLock(共享)和 WriteLock(独享)的组合,遵循 "读读共享、读写互斥、写写互斥" 的原则,能极大提升读多写少场景的性能。
    • 升级 :JDK 8 引入了性能更好的 StampedLock,提供了乐观读、读写锁转换等更灵活的功能。

5. 自旋锁 vs 互斥锁

自旋锁: 当没拿到锁时, 不会释放CPU, 而是一直处于活跃状态(while循环), 一直循环判断锁是否被释放. 循环时间一长十分消耗CPU资源.

互斥锁: 没拿到锁就释放CPU, 进入阻塞等待状态, 直到被唤醒.

二、关键锁的实现与原理(进阶高频)

1. synchronized 关键字

  • 用法:修饰实例方法、静态方法、代码块。
  • 原理:JVM 层面实现。
  • 锁升级过程(重点!) :为了在性能和开销间取得平衡,JDK 1.6 后引入了"偏向锁 -> 轻量级锁 -> 重量级锁"的升级过程,锁只能升级不能降级。
    • 无锁:新对象。
    • 偏向锁:假设只有一个线程访问,会在对象头Mark Word记录线程ID。后续该线程进入/退出同步块只需要检查ID,无需CAS。
    • 轻量级锁:当有第二个线程竞争时,升级为轻量级锁。通过CAS自旋(适应性自旋)尝试获取锁,避免直接进入内核态阻塞。
    • 重量级锁 :自旋失败或竞争激烈时,升级为重量级锁(操作系统层面的互斥量 Mutex),线程进入阻塞队列,性能消耗最大。
  • 面试点synchronizedReentrantLock 的区别。
    先尝试偏向锁,降低无竞争的开销;出现竞争时,升级为轻量级锁,通过自旋避免阻塞;自旋失败(竞争加剧),最终升级为重量级锁,让线程阻塞。当 synchronized 升级为重量级锁时,它需要通过操作系统的互斥锁(Mutex Lock)来实现线程的阻塞和唤醒
    2. ReentrantLock(AQS 的代表)
  • 核心 :基于 AbstractQueuedSynchronizer(AQS) 实现。AQS 内部维护了一个 volatile int state(同步状态)和一个 CLH 变体的FIFO双向队列(阻塞线程队列)。
  • 原理
    • 加锁 :通过 lock() -> acquire() -> tryAcquire() 尝试获取锁(修改 state 从0到1),成功则独占;失败则通过 addWaiter() 将线程封装为 Node 加入队列尾部,并通过 acquireQueued() 进行自旋或阻塞。
    • 解锁unlock() -> release() -> tryRelease()state 减为0,并 unparkSuccessor() 唤醒队列中的下一个线程。
  • 优势 :相比 synchronized,提供了更多功能:可中断 (lockInterruptibly())、可定时 (tryLock(timeout))、可设置公平/非公平支持多个条件变量 (Condition)。

3. CAS(Compare And Swap)与原子类

  • CAS:乐观锁的核心实现,是一条CPU原子指令。操作包含三个值:内存位置 V、预期原值 A、新值 B。仅当 V == A 时,才将 V 更新为 B。
  • 原子类java.util.concurrent.atomic 包下的类(如 AtomicInteger)使用 CAS 实现无锁线程安全操作。
  • 缺点
    • ABA问题 :值从A变成B又变回A,CAS会认为没变。解决方案是使用 AtomicStampedReference (带版本戳)。
    • 循环时间长开销大:自旋CAS如果长时间不成功,会消耗CPU。
    • 只能保证一个共享变量的原子操作 。可以用 AtomicReference 封装多个变量。
相关推荐
檐下翻书1731 小时前
集团组织架构图在线设计 多部门协作编辑工具
大数据·论文阅读·人工智能·物联网·架构·流程图·论文笔记
小王毕业啦2 小时前
2008-2023年 全国统一大市场发展水平
大数据·人工智能·数据挖掘·数据分析·数据统计·社科数据·实证数据
青云交2 小时前
Java 大视界 -- Java 大数据在智能医疗影像数据标注与疾病辅助诊断模型训练中的应用
java·大数据·多模态融合·医疗影像标注·辅助诊断·临床 ai·dicom 处理
摇滚侠2 小时前
ElasticSearch 教程入门到精通,测试工具、倒排索引、索引创建查询删除,笔记6、7、8、9
大数据·笔记·elasticsearch
大卫小东(Sheldon)2 小时前
SQL查询中的窗口函数(主要以 PostgreSQL 为例)
大数据·sql·postgre
张人玉2 小时前
大数据Hadoop系列——在ubuntu上安装pig数据库
大数据·hadoop·ubuntu·pig
智链RFID2 小时前
RFID资产管理系统:智能管理新利器
大数据·人工智能
一个天蝎座 白勺 程序猿2 小时前
KingbaseES在国家电网领域的深度应用与实践——国家电网新一代集控系统
大数据·数据迁移·kingbase·金仓数据库
李慕婉学姐3 小时前
【开题答辩过程】以《基于Hadoop的医生相关数据分析与可视化及医生推荐系统》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
大数据·hadoop·数据分析