Java锁机制:synchronized与ReentrantLock深度对比

Java锁机制:synchronized与ReentrantLock深度对比

分类:concurrent
摘要:锁是并发编程的核心概念,理解synchronized和ReentrantLock的区别和适用场景对写出高质量并发代码至关重要。本文将从底层原理、功能差异、实战代码及性能维度进行全面解析。


一、 引言

在Java并发编程的世界里,线程安全是不可逾越的大山,而“锁”则是攀登这座大山最核心的工具。提到Java锁,最经典的莫过于JVM原生提供的 synchronized 关键字和 JDK 1.5 引入的 ReentrantLock 类。

很多开发者在面试或实际开发中常常困惑:既然有了 synchronized,为什么还要造一个 ReentrantLock?它们到底有什么本质区别?在什么场景下该选择哪一个?本文将剥离表象,深入JVM底层与AQS原理,为你深度对比这两者,助你彻底掌握Java锁机制。


二、 核心概念与基础认知

1. synchronized:JVM原生基石

synchronized 是Java语言内置的关键字,属于JVM层面的实现。它就像一把自动化的防盗门,你只需要告诉它“锁哪里”,剩下的加锁、解锁过程完全由JVM负责。

  • 特性:隐式加锁/解锁、可重入、非公平锁(默认)、不可中断。
  • 作用范围:修饰普通方法(锁当前实例对象)、修饰静态方法(锁Class对象)、修饰代码块(指定锁对象)。

2. ReentrantLock:API层面的精密仪器

ReentrantLockjava.util.concurrent.locks 包下的一个类,基于AQS(AbstractQueuedSynchronizer)实现。它就像一把带各种高级功能的密码锁,需要开发者手动控制开启和关闭。

  • 特性:显式加锁/解锁、可重入、支持公平/非公平选择、支持响应中断、支持超时获取锁。
  • 核心APIlock(), unlock(), tryLock(), lockInterruptibly()

三、 底层实现原理深度剖析

要真正理解两者的区别,必须深入到底层看源码。

1. synchronized 的对象头与 Monitor

synchronized 的实现依赖于对象的 Monitor(监视器锁)。在JVM中,对象在内存中的布局分为:对象头、实例数据和对齐填充。

  • 对象头:包含 Mark Word(存储锁状态、哈希值、GC年龄等)和类型指针。
  • 锁升级过程:JDK 1.6 之后,为了优化 synchronized 的性能,引入了“锁升级”机制:
    1. 无锁:对象刚创建。
    2. 偏向锁:同一线程多次获取锁,只需修改Mark Word,无需CAS。
    3. 轻量级锁:不同线程交替执行,通过CAS将Mark Word替换为指向栈中Lock Record的指针。
    4. 重量级锁:竞争激烈,Mark Word指向堆中的Monitor对象,线程会阻塞,涉及用户态与内核态的切换,开销较大。

字节码层面,synchronized 代码块通过 monitorentermonitorexit 两个指令实现。

2. ReentrantLock 与 AQS 架构

ReentrantLock 的核心在于 AQS(抽象队列同步器)

  • state 变量:AQS中维护了一个 volatile int state 变量。state=0 表示锁空闲;state>0 表示锁被占用,值代表重入次数。
  • CLH 队列:当锁竞争失败时,线程会被封装成 Node 节点,加入到一个双向链表(CLH队列变体)中,并调用 LockSupport.park() 挂起线程。
  • CAS 操作:获取锁的过程本质上是利用 CAS(Compare And Swap)原子指令修改 state 变量。
  • 公平性实现
    • 非公平锁(默认):直接尝试CAS抢锁,抢不到再排队。性能高,但可能导致线程饥饿。
    • 公平锁:严格按照队列顺序获取锁,先来后到。

四、 核心区别对比

为了更直观地展示差异,我们整理如下对比表:

维度 synchronized ReentrantLock
实现层面 JVM关键字,C++实现 Java API类,基于AQS
锁的获取/释放 自动释放(JVM控制),代码块结束或异常时释放 必须手动释放(finally块中),否则死锁
锁的类型 仅仅支持非公平锁 支持公平锁和非公平锁(构造函数传参)
中断响应 不支持,线程阻塞后无法中断,只能一直等 支持 lockInterruptibly(),解决死锁僵局
超时机制 不支持 支持 tryLock(timeout),获取不到立即返回
条件变量 单一条件,通过 wait/notify 实现 支持多个 Condition,可精细控制线程等待/唤醒
性能 JDK 1.6优化后,低竞争下性能优异 高竞争下,AQS的CAS+自旋性能更稳定

五、 实战代码演练

场景一:基础加锁与手动解锁对比

synchronized 示例:
```java
/*
* synchronized 基础示例
* 模拟银行转账,synchronized 自动管理锁
/
public class SynchronizedDemo {
private int balance = 0;

// 修饰实例方法,锁对象为当前实例
public synchronized void deposit(int amount) {
    // 进入方法自动加锁
posted @ 2026-02-27 00:01  寒人病酒  阅读(10)  评论(0)    收藏  举报