Java所有的锁:从基础到进阶

张开发
2026/4/16 14:04:59 15 分钟阅读

分享文章

Java所有的锁:从基础到进阶
作为Java开发者“锁”是绕不开的核心知识点——不管是日常开发中的线程安全还是面试时的高频追问比如synchronized升级、CAS原理、锁的选型掌握所有锁的特性和适用场景才能真正吃透并发编程。很多人对锁的认知只停留在“synchronized和Lock”的表面分不清公平锁与非公平锁、乐观锁与悲观锁更不懂锁升级的逻辑导致开发中选型失误、性能瓶颈面试时被问得哑口无言。今天这篇文章一次性讲透Java中所有锁按“认知→分类→原理→实战→避坑”的逻辑从基础到进阶覆盖所有高频锁类型结合代码示例和面试考点看完直接拿捏锁的核心知识再也不用零散查资料。为什么需要锁在Java并发编程中线程安全的核心诉求是解决“多线程并发访问共享资源”的冲突问题——比如多个线程同时修改同一个变量、操作同一个文件会导致数据不一致、竞态条件等问题。锁的本质就是通过“限制共享资源的并发访问”保证同一时刻只有符合条件的线程能操作资源从而避免上述问题。简单说锁就是并发编程的“线程安全守护神”。随着Java并发体系的完善锁的分类越来越丰富不同类型的锁适用于不同的并发场景。我们按“高频度、高实用性”的维度逐一拆解所有锁类型拒绝模糊表述全程干货无废话。第一维度按同步语义分类核心面试必问这是锁最核心的分类核心区别在于“对并发冲突的预期”直接决定了锁的底层实现逻辑与性能特性也是日常开发中锁选型的核心依据。1. 悲观锁假设冲突一定会发生核心语义先加锁再操作。悲观锁认为多线程并发时数据修改冲突是必然的所以在操作共享资源前必须先获取锁阻塞其他线程直到当前线程操作完成并释放锁。底层实现依赖操作系统的互斥量Mutex或信号量存在内核态与用户态的上下文切换开销性能相对较低但安全性极高。典型实现synchronizedJava原生关键字JVM层面实现自动加锁、释放锁无需手动管理上手简单。ReentrantLockJUC包下的可重入锁默认是悲观锁语义支持手动加锁、释放锁提供中断、超时等高级功能。ReentrantReadWriteLock.WriteLock写锁读写锁中的写锁属于悲观锁的一种排他性强。代码示例synchronized与ReentrantLock适用场景高冲突、写操作频繁的场景如金融转账、库存扣减安全性优先于性能。2. 乐观锁假设冲突不会发生核心语义先操作后校验。乐观锁认为多线程并发时数据修改冲突的概率很低所以无需提前加锁直接操作共享资源仅在提交操作时检查资源是否被修改。底层实现基于CASCompare and Swap比较并交换原子操作或版本号机制无阻塞、无上下文切换开销性能优于悲观锁但需要处理冲突重试逻辑。典型实现Atomic系列原子类AtomicInteger、AtomicLong等底层基于CAS实现无需加锁保证并发安全。ConcurrentHashMap分段锁CAS结合兼顾并发性能和安全性。StampedLockJUC包下的高级锁支持乐观读模式性能优于传统读写锁。版本号/时间戳机制常见于数据库乐观锁如JPA的Version注解。代码示例AtomicInteger与版本号机制面试高频提醒乐观锁并非“无锁”而是“无阻塞锁”悲观锁并非“低效锁”而是“兜底锁”二者无绝对优劣核心是根据并发冲突频率选型。适用场景低冲突、读操作频繁的场景如缓存更新、计数器性能优先于安全性。第二维度按锁的排他性分类核心区别在于“同一时刻允许多少个线程获取锁”直接决定了锁的并发能力也是JUC包下锁的核心设计维度基于AQS的两种核心模式。1. 独占锁排他锁同一时刻仅一个线程持有核心语义同一时刻只有一个线程能获取锁其他线程获取失败则进入等待队列具有强排他性类似“一人使用其他人排队”主要用于写操作。底层依赖基于AQS的独占模式tryAcquire/tryRelease钩子方法。典型实现synchronized、ReentrantLock、ReentrantReadWriteLock.WriteLock写锁。核心特点安全性高但并发能力弱适合写操作场景如修改数据、删除数据。2. 共享锁同一时刻多个线程持有核心语义同一时刻允许多个线程同时获取锁无排他性类似“多人同时阅读同一本书”仅阻止写线程获取锁主要用于读操作。底层依赖基于AQS的共享模式tryAcquireShared/tryReleaseShared钩子方法。典型实现ReentrantReadWriteLock.ReadLock读锁、Semaphore信号量、CountDownLatch倒计时器。核心特点并发能力强适合读多写少场景如查询数据、统计数据。第三维度按锁的公平性分类核心区别在于“线程获取锁的顺序”即是否遵循“先到先得FIFO”原则影响锁的公平性与性能面试中经常被问“synchronized是公平锁还是非公平锁”。1. 公平锁先到先得拒绝插队核心语义线程获取锁的顺序严格遵循“FIFO”原则先请求锁的线程先获取锁不允许“插队”能避免线程饥饿长期得不到锁。底层实现线程获取锁时先检查等待队列是否有前驱线程若有则排队无则尝试获取锁。典型实现ReentrantLock构造方法传入true、ReentrantReadWriteLock公平模式。核心特点公平性高但频繁切换线程会增加性能开销吞吐量较低适合对公平性要求高的场景如金融交易。2. 非公平锁允许插队优先性能核心语义线程获取锁时不遵循FIFO原则允许“插队”——刚释放锁的线程可再次立即获取锁无需排队减少线程切换开销。底层实现线程获取锁时先尝试CAS获取锁失败再加入等待队列减少线程切换开销。典型实现synchronized默认非公平、ReentrantLock默认非公平构造方法传入false。核心特点公平性低可能导致线程饥饿但性能高、吞吐量高适用于大多数并发场景日常开发默认选择。第四维度按锁的重入性分类核心区别在于“持有锁的线程是否能再次获取该锁”避免线程自身死锁是锁的基础特性很多新手容易忽略这一点。1. 可重入锁持有锁的线程可再次获取核心语义持有锁的线程可再次获取该锁重入锁内部会记录“重入次数”释放时需释放对应次数直到重入次数为0锁才真正释放。底层实现通过记录“持有锁的线程ID”和“重入次数”避免线程自身死锁。典型实现synchronized可重入、ReentrantLock可重入名字就带Reentrant、ReentrantReadWriteLock。核心优势避免线程自身死锁简化嵌套同步逻辑如方法A调用方法B二者均加锁可重入锁不会导致死锁。代码示例可重入锁嵌套2. 不可重入锁持有锁的线程无法再次获取核心语义持有锁的线程无法再次获取该锁若尝试获取会导致自身死锁。典型实现JUC包下无直接实现自定义锁可实现不可重入语义如简单的CAS锁、Linux下的pthread_mutex_t默认不可重入。核心劣势易导致线程自身死锁适用场景极少仅用于特殊同步需求日常开发几乎不用。第五维度按底层实现分类JVM锁升级重点核心区别在于“锁的实现层面”直接决定锁的性能开销尤其是synchronized的锁升级机制是面试高频难点JDK1.6后重大优化。核心前提锁升级是不可逆的一旦从偏向锁升级到轻量级锁就不能再回退一旦升级到重量级锁就只能一直保持重量级锁状态。1. 偏向锁无竞争场景的最优解核心场景当只有一个线程反复获取、释放同一把锁没有任何其他线程参与竞争时如单线程执行同步代码块JVM会自动使用偏向锁。核心原理“偏心”绑定线程——JVM在对象头Mark Word中记录当前持有锁的线程ID后续该线程再获取锁时无需做任何同步操作不用CAS、不用切换内核态直接进入同步代码块开销几乎可以忽略不计。优势与不足开销极小适合无竞争场景但一旦有其他线程参与竞争需撤销偏向锁触发升级撤销过程有一定开销。2. 轻量级锁轻度竞争场景的过渡方案核心场景有少量线程交替获取锁没有同时争抢即“自旋竞争”此时偏向锁被撤销升级为轻量级锁。核心原理线程进入同步代码块前会在自己的栈帧中创建“锁记录”Lock Record记录锁对象的Mark Word副本通过CAS操作将锁对象的Mark Word更新为自己的锁记录地址CAS成功则获取锁失败则进入自旋不断尝试CAS。优势与不足无需切换内核态开销远小于重量级锁但自旋会消耗CPU资源若锁持有时间过长自旋会变成无效消耗触发升级为重量级锁。3. 重量级锁重度竞争场景的终极保障核心场景有大量线程同时争抢同一把锁重度竞争或锁被持有时间很长自旋无法高效获取锁时轻量级锁升级为重量级锁。核心原理依赖操作系统的互斥量Mutex Lock属于内核态锁。线程获取锁失败时不再自旋而是直接放弃CPU执行权进入操作系统的阻塞队列直到持有锁的线程释放锁操作系统才会唤醒队列中的线程。优势与不足稳定性高能应对重度竞争但开销最大每次获取、释放锁都需要切换用户态和内核态线程阻塞/唤醒开销大。锁升级完整流程必背无锁 → 偏向锁无竞争 → 轻量级锁轻度竞争CAS自旋 → 重量级锁重度竞争阻塞等待一旦升级无法回退。第六维度其他高频锁类型面试拓展1. 自旋锁轻量级锁的核心实现核心语义线程获取锁失败时不立即阻塞而是循环尝试获取锁自旋直到获取成功或达到自旋阈值。底层实现基于CAS操作无上下文切换开销适合锁持有时间短、竞争不激烈的场景自旋次数达到阈值JVM默认10次则升级为重量级锁。注意JDK1.6后引入“自适应自旋锁”自旋次数会根据之前的竞争情况动态调整无需手动配置。2. 分段锁ConcurrentHashMap的核心优化核心语义将锁的粒度细化把一个大的容器如HashMap分成多个段Segment每个段对应一把锁多线程访问不同段时无需竞争同一把锁提高并发性能。典型实现JDK1.7的ConcurrentHashMapJDK1.8后改为CAS synchronized取消分段锁但分段锁的思想仍需掌握。3. 无锁乐观锁的极致形态核心语义完全不依赖锁机制直接通过CPU原子指令如CAS或线程本地存储ThreadLocal保证线程安全不存在锁竞争性能最优。典型实现Atomic系列原子类、Unsafe.CAS、ThreadLocal线程隔离无共享则无竞争。注意无锁并非“不需要保证安全”而是通过非锁机制实现安全适用于线程间无共享数据修改或修改冲突极少的场景。实战总结锁的选型指南开发/面试都能用掌握了所有锁的特性后核心是“按需选型”避免盲目使用重量级锁也避免滥用乐观锁导致冲突问题。整理了一张选型表直接套用面试高频避坑点必记避坑1synchronized不是只有重量级锁JDK1.6后有偏向锁、轻量级锁、重量级锁三种状态会自动升级。避坑2乐观锁不是无锁而是无阻塞锁需要处理冲突重试逻辑否则会导致数据不一致。避坑3ReentrantLock必须在finally中释放锁否则会导致锁泄漏异常时无法释放锁。避坑4锁升级是不可逆的一旦升级为重量级锁就不会再回退到偏向锁或轻量级锁。避坑5synchronized是自动重入、非公平锁无法手动设置为公平锁ReentrantLock可手动设置公平/非公平。

更多文章