乐观锁核心是假设并发冲突极少发生,不加物理锁,通过版本号 / 时间戳控制并发。实际开发中,给数据加 version 字段,更新时先比对版本,一致则更新并自增版本,不一致说明数据被修改,直接重试或返回失败。它无锁竞争、无线程阻塞,在 Java 中常用 Atomic 原子类、CAS 操作实现,适合读多写少的场景,比如电商商品库存、用户信息更新。相比悲观锁,它大幅降低锁开销,提升并发性能,只会在写冲突时处理,完美解决高并发读场景下的线程安全问题,是分布式、高并发系统的首选方案。
悲观锁核心是假设并发冲突一定会发生,每次操作数据都先加锁,独占资源后再处理,其他线程必须等待。Java 中通过 synchronized、ReentrantLock 实现,主要解决写多读少、并发冲突极高的场景问题,比如金融转账、库存扣减、订单创建等关键业务,要求数据绝对安全,不允许脏写、脏读。它能强制串行执行临界区代码,彻底避免并发修改导致的数据不一致、超卖、重复扣款等严重问题。缺点是线程阻塞、上下文切换开销大,并发性能低,但数据安全性拉满,是核心业务的兜底保障。
自旋锁是线程获取锁失败时,不进入阻塞状态,而是循环重试获取锁,避免线程上下文切换的开销。它的适用场景非常明确:临界区代码执行时间极短、线程竞争不激烈的场景,比如 JDK 内置的原子类、轻量级锁底层实现。因为自旋会消耗 CPU,不适合长时间持有锁的场景。在多核心 CPU 环境下,自旋锁效果最好,短时间等待就能获取锁,相比阻塞锁,大幅提升性能。如果锁竞争激烈、执行时间长,自旋会浪费 CPU 资源,绝对不建议使用。
可重入锁最大特点是同一个线程可以多次获取同一把锁,不会自己锁死自己,Java 中 synchronized、ReentrantLock 都是可重入锁。它会维护锁计数器,线程每获取一次锁计数 +1,释放一次 -1,计数为 0 才真正释放锁。重要性体现在:解决了线程递归调用、同步方法嵌套调用的死锁问题,让锁的使用更安全灵活。比如一个同步方法调用另一个同类同步方法,可重入锁能保证线程顺利执行,不用额外处理锁竞争。它是 Java 锁机制的基础,大幅降低了并发编程的出错概率,是开发中最常用的锁特性。
读写锁把锁分为读锁和写锁,遵循读共享、写独占规则:多个线程可以同时加读锁,保证高并发读;写锁是独占锁,读写、写写互斥。Java 中用 ReentrantReadWriteLock 实现,完美适配读多写少的场景,比如缓存、配置信息、商品列表查询。相比独占锁,它消除了读操作的锁竞争,让并发读能力提升数倍,写操作又能保证数据安全。读写锁平衡了并发性能和数据一致性,比全量加锁高效很多,是高并发读场景下提升系统吞吐量的核心方案。
公平锁是线程按照申请锁的顺序排队获取锁,先到先得,不会出现线程饥饿。Java 中 ReentrantLock 通过构造方法传入 true 实现公平锁,底层基于 AQS 队列,严格按 FIFO 顺序分配锁。它的优点是公平公正,所有线程都能获取到锁;缺点是性能远低于非公平锁,因为要维护排队顺序,线程上下文切换频繁,CPU 开销大。公平锁适合对执行顺序有要求、避免线程饥饿的场景,日常高并发业务极少使用,毕竟性能损耗会直接降低系统吞吐量。
非公平锁是线程随机抢占锁,效率高;公平锁严格排队,无饥饿。核心差异:非公平锁吞吐量高、开销小,大概率出现线程插队;公平锁公平性好,性能差、切换开销大。选择依据很简单:追求并发性能选非公平锁,这是 Java 默认方案,适合绝大多数业务;必须保证线程执行顺序、杜绝饥饿才用公平锁,比如定时任务、有序处理场景。日常开发 99% 的场景用非公平锁,只有特殊业务逻辑才考虑公平锁,平衡性能和业务需求是核心。
共享锁也叫读锁,允许多个线程同时持有,Java 读写锁的读锁就是典型共享锁。使用场景:读多写少、数据读取无需修改的并发场景,比如缓存查询、配置读取、静态数据访问。优势是彻底消除读操作的锁竞争,让多个线程可以并行读取数据,大幅提升系统并发读能力,同时和写锁互斥,保证数据修改时的安全性。相比独占锁,共享锁的并发性能呈指数级提升,既保证了线程安全,又最大化利用系统资源,是高并发读场景的最优选择。
独占锁也叫排他锁,同一时间只能有一个线程持有,synchronized、ReentrantLock 都是独占锁。多线程环境下,它会强制临界区代码串行执行:线程获取锁后,其他线程必须阻塞等待,直到锁被释放。它严格管理共享资源的访问,杜绝并发修改、脏读脏写,保证数据绝对一致。比如订单创建、金额扣减、数据插入等写操作,必须用独占锁。它的管理方式简单粗暴,安全性拉满,但并发能力差,适合写操作频繁、数据安全要求极高的核心业务场景。
重量级锁是 JVM依赖操作系统互斥量实现的锁,线程竞争失败会进入阻塞状态,涉及用户态和内核态切换,开销极大。应用场景:线程竞争激烈、临界区代码执行时间长的场景,比如复杂计算、IO 操作、长事务处理。对性能的影响:上下文切换开销巨大,并发能力极低,线程阻塞会导致系统吞吐量下降。它是 JVM 锁的最终形态,只有在偏向锁、轻量级锁都失效后才会升级为重量级锁,非必要不使用,是高并发场景下的性能瓶颈。
轻量级锁是 JVM基于 CAS 自旋实现的锁,无操作系统阻塞,开销远低于重量级锁。优势:线程竞争时通过自旋重试,避免用户态内核态切换,上下文切换开销极小,执行速度快。使用条件:线程竞争不激烈、临界区代码执行时间极短,多个线程交替获取锁,几乎不发生同时抢占锁的情况。它是 JVM 默认的锁优化方案,在轻度并发场景下,性能远超重量级锁。一旦锁竞争加剧,自旋失败,轻量级锁会立即升级为重量级锁,适配不同并发强度。
偏向锁是 JVM 的极致锁优化,假设锁只有一个线程使用,第一次获取锁后,后续该线程再次获取锁,无需任何 CAS 操作,直接放行。作用是消除无竞争场景下的锁开销,把锁性能降到最低。在性能优化中,它是第一优先级的优化方案,针对单线程访问同步代码的场景,比如单线程工具类、初始化方法,几乎无锁开销。当有第二个线程竞争锁时,偏向锁会立即撤销,升级为轻量级锁。它是 JVM 针对无竞争场景的专属优化,大幅提升单线程同步代码的执行效率。
分段锁是把一个大锁拆分成多个小锁,每个分段独立加锁,Java 中 ConcurrentHashMap 底层就是分段锁(JDK8 之前)。传统集合用一把锁,所有线程竞争;分段锁把数据分成多个段,操作不同段的线程可以并行执行,只有同一分段的线程才会竞争。它大幅降低了锁竞争概率,让并发访问能力成倍提升,既保证了线程安全,又兼顾高性能。分段锁适合大型集合、缓存等共享数据结构,是高并发集合框架的核心设计,完美平衡了安全和性能。
死锁是多个线程互相持有对方需要的锁,无限等待。预防方法:固定锁的获取顺序,所有线程按相同顺序加锁;限时获取锁,用 tryLock(time),超时放弃并释放已持有的锁;减少锁嵌套,避免同时持有多把锁。解决方法:发生死锁后,只能重启服务,所以核心在预防。开发中还可以通过线程池监控、死锁检测工具(jstack)排查问题。遵循锁的使用规范,避免嵌套锁、循环等待,是杜绝死锁的关键,死锁是并发编程最严重的问题,必须严格规避。
锁粗化是 JVM 的自动锁优化,把多个连续的加锁、解锁操作合并成一个大锁,减少锁操作次数。应用场景:同一个线程频繁获取、释放同一把锁,比如循环内的同步代码。优点:降低频繁加锁解锁的开销,提升执行效率;缺点:锁持有时间变长,可能增加其他线程的等待时间。JVM 会自动判断是否粗化,只在无竞争或轻度竞争时使用。它是无感知的性能优化,适合循环同步、连续同步方法调用的场景,平衡了锁开销和等待时间。
锁消除是 JVM通过逃逸分析自动删除无用锁的优化。原理:JVM 检测到同步代码块中的共享对象,只在当前线程内使用,不会逃逸到其他线程,就判定无并发竞争,直接消除锁。比如方法内的局部对象调用同步方法,JVM 会直接去掉锁逻辑。对性能的影响:彻底消除无意义的锁开销,让代码执行效率和无锁代码一致。它是 JVM 极致的锁优化,无需开发者干预,自动优化无用锁,大幅提升单线程、无竞争同步代码的性能。
条件锁是配合独占锁使用的线程协调工具,Java 中 Condition 接口实现,通过 lock.newCondition() 创建。它替代了 Object 的 wait/notify,支持多个等待队列,线程可以等待指定条件,唤醒指定线程。核心方法:await()让线程等待,signal()/signalAll() 唤醒等待线程。常用于生产者 - 消费者模式:队列满时生产者等待,队列空时消费者等待,条件满足后互相唤醒。它让线程间的协调更精准,比传统 wait/notify 更灵活,是复杂并发协作场景的核心组件。
阻塞锁:线程获取锁失败,立即进入阻塞状态,释放 CPU,synchronized、重量级锁都是阻塞锁。非阻塞锁:线程获取锁失败,不阻塞,通过自旋 /CAS 重试,乐观锁、轻量级锁属于非阻塞锁。区别:阻塞锁 CPU 开销小,但上下文切换慢;非阻塞锁无切换开销,但自旋消耗 CPU。场景:阻塞锁适合锁竞争激烈、临界区执行时间长;非阻塞锁适合竞争少、执行时间短。Java 会自动根据竞争强度切换,开发中无需手动处理,是锁机制的底层设计。
StampedLock 是 JDK8 新增的读写锁增强版,比 ReentrantReadWriteLock 性能更高,支持乐观读、悲观读、写锁三种模式,无重入性。核心特性:乐观读无锁,性能极致;支持写锁降级为读锁,不支持读升级写;通过 long 类型的 stamp 标记锁状态。应用场景:超高并发读、极少写的场景,比如缓存、共享配置、热点数据查询。它解决了传统读写锁读阻塞写的问题,乐观读完全无锁,写操作也能及时执行,是高并发读场景下性能最强的锁。
乐观写锁基于 CAS/ 版本号实现,不加锁,冲突时重试,在写并发极低、读并发极高的场景下,远优于悲观写锁。比如用户个人信息修改、商品详情更新,写操作极少,读操作极多。悲观写锁会阻塞所有读操作,并发性能极差;乐观写锁无锁竞争,不影响读操作,只有写冲突时才重试。它无阻塞、无上下文切换,大幅提升系统整体吞吐量。但写冲突频繁时,重试会消耗 CPU,此时不如悲观写锁,核心看写并发量。
互斥锁就是独占锁,同一时间只允许一个线程访问临界区,是 Java 同步控制的基础核心。它的角色是强制共享资源串行访问,彻底杜绝并发修改、数据竞争,保证线程安全。synchronized、ReentrantLock 都是互斥锁,所有需要保证数据一致性的写操作,都必须依赖互斥锁。它是并发编程的基石,没有互斥锁,多线程修改共享数据会出现脏读、脏写、数据错乱。它牺牲了并发性能,换取了绝对的数据安全,是所有同步机制的核心。
Semaphore 不是锁,是并发控制器,用来限制同时访问资源的线程数量。核心方法:acquire()获取许可,release() 释放许可。作用:控制接口、连接、线程池的并发数,防止系统过载。场景:数据库连接池、接口限流、资源池管理,比如限制同时只有 10 个线程访问数据库。它可以实现公平 / 非公平模式,是分布式、高并发系统限流的核心工具,比锁更灵活,专门用于控制并发量级,保护核心资源不被压垮。
CountDownLatch 是线程同步工具,让一个或多个线程等待,直到其他线程完成指定操作。工作机制:初始化计数器,countDown()计数器 -1,await() 线程等待计数器为 0。用途:主线程等待所有子线程执行完毕再汇总结果、多服务启动后执行主逻辑、并发测试。比如统计多线程执行时间、加载多个配置文件后启动服务。它是一次性工具,计数器归零后无法重置,适合等待固定数量的线程完成任务,是并行任务汇总的常用工具。
CyclicBarrier 是可循环使用的线程屏障,让一组线程互相等待,全部到达屏障后再一起执行。核心方法:await() 线程到达屏障并等待,所有线程都到达后,屏障打开,全部放行。它可以重置重复使用,还支持屏障动作。用途:多线程分批计算、并行测试、游戏帧同步。比如 4 个线程同时处理数据,都处理完后再汇总结果。它和 CountDownLatch 相反,是线程间互相等待,适合多线程分步协作、循环执行的并行场景。
Exchanger 是线程间数据交换工具,两个线程到达交换点时,互相交换对方的数据。功能:实现两个线程的安全数据传输,无锁、高效。场景:生产者 - 消费者模式(两个线程交换数据)、遗传算法、管道流数据交换、线程间数据校对。比如一个线程生产数据,另一个线程处理数据,通过 Exchanger 交换缓冲区。它只支持两个线程交换,简单高效,是专用的线程间数据同步工具,适合成对线程的数据交互场景。
适应性自旋锁是 JVM 对自旋锁的智能优化,自旋次数不是固定的,而是根据历史自旋结果动态调整。如果线程之前自旋成功获取过锁,JVM 会增加自旋次数;如果自旋经常失败,就减少自旋次数,甚至直接阻塞线程。它智能判断锁的竞争强度,避免无效自旋浪费 CPU,在轻度竞争时自旋提升性能,重度竞争时阻塞节省资源。这是 JVM 自动完成的优化,无需开发者干预,完美平衡了自旋的性能优势和 CPU 开销。
JVM 锁会根据竞争强度自动升级:偏向锁→轻量级锁→重量级锁,无竞争用偏向锁,轻度竞争用轻量级锁,重度竞争用重量级锁,自动适配并发场景,兼顾性能和安全。锁降级只在安全点、垃圾回收时发生,把重量级锁降级为轻量级锁。升级是实时的,降级是被动的,整个过程由 JVM 自动完成,开发者无感知。这种机制让锁在不同并发下都能最优工作,低并发无开销,高并发保安全,实现了灵活的线程同步控制。
数据库锁分共享锁(读锁)、排他锁(写锁)、行锁、表锁、间隙锁。共享锁允许多读,排他锁独占;行锁锁单行,性能高;表锁锁全表,开销小;间隙锁防止幻读。在事务中,锁保证 ACID 属性:通过排他锁保证原子性、一致性,共享锁保证读一致性,间隙锁解决幻读。Java 中通过 @Transactional、for update 语句加锁,解决并发事务的脏读、不可重复读、幻读问题。数据库锁是事务安全的核心,平衡了并发性能和数据一致性。
无锁编程是不使用互斥锁,通过 CAS、版本号、线程封闭实现线程安全,Java 中 Atomic 原子类就是无锁编程的实现。概念:完全抛弃阻塞锁,用硬件级 CAS 操作保证原子性,线程永不阻塞。优势:无线程阻塞、无上下文切换、无死锁风险,并发性能极致,CPU 利用率极高。适合读多写少、高并发场景,比如计数器、ID 生成器、状态标记。无锁编程是 Java 高并发的最优方案,唯一缺点是写冲突时需要重试,实现逻辑比锁复杂。
混合锁是结合乐观锁、悲观锁、读写锁等多种机制的智能锁,根据运行时的并发情况自动切换。比如 StampedLock,同时支持乐观读、悲观读、写锁;JDK8 的 ConcurrentHashMap 结合 CAS+synchronized。它的核心逻辑:读用乐观锁,轻度写用轻量级锁,重度写用悲观锁。混合锁自动适配不同并发场景,读场景无锁高性能,写场景加锁保安全,完美平衡性能和安全。它是现代并发框架的主流设计,比单一锁机制更灵活、更高效。
文件锁是操作系统级别的锁,Java 通过 FileChannel 的 lock()/tryLock() 实现,跨进程生效,不同 JVM、不同程序都能识别。它分共享读锁、独占写锁,控制文件的并发读写:多个进程可以同时读,写操作独占锁。作用:防止多进程同时写文件导致数据错乱、日志覆盖、配置文件冲突。比如日志写入、配置更新、文件上传,必须用文件锁保证安全。它是进程间文件同步的唯一方案,解决了跨程序文件并发访问的问题。
内核锁是操作系统内核实现的互斥锁,也叫重量级锁,Java 的 synchronized 在竞争激烈时,最终会调用内核锁。它工作在操作系统内核态,通过互斥量实现,线程竞争失败会被操作系统挂起,进入阻塞队列,释放 CPU。作用:在操作系统层面强制线程串行访问临界资源,保证多进程、多线程的绝对安全。缺点是用户态和内核态切换开销极大,性能极低。它是锁的最终保障,只有用户态锁无法解决竞争时,才会使用内核锁。