Java 并发之 ReentrantLock:实现逻辑、公平性及与 synchronized 的异同
·
Java 并发之 ReentrantLock:实现逻辑、公平性及与 synchronized 的异同
摘要:在Java并发编程中,
ReentrantLock和synchronized都是实现线程同步的核心机制,用于解决多线程竞争共享资源的问题。但它们的实现原理和使用方式有显著区别,尤其ReentrantLock提供了更灵活的同步控制能力。
一、ReentrantLock的实现原理
ReentrantLock是JDK提供的显式锁(需要手动获取和释放),其核心实现依赖于AQS(AbstractQueuedSynchronizer,抽象队列同步器)。简单来说,AQS是一个"模板类",定义了一套多线程访问共享资源的同步框架,ReentrantLock通过继承AQS并实现其抽象方法来完成锁的逻辑。
核心原理拆解:
-
AQS的核心组件:
- 状态变量(state):一个int类型的变量,用于表示锁的状态。
ReentrantLock是"可重入锁",所以state记录当前线程获取锁的重入次数(初始为0,获取锁后变为1,再次获取则递增,释放时递减,直到0表示锁被完全释放)。 - 等待队列:当多个线程竞争锁失败时,会被封装成节点(Node)加入一个双向链表队列,等待被唤醒(类似"排队等待")。
- 当前持有锁的线程(exclusiveOwnerThread):记录当前获取到锁的线程,用于实现"可重入"和"独占性"。
- 状态变量(state):一个int类型的变量,用于表示锁的状态。
-
锁的获取与释放流程:
- 获取锁(lock()):
- 线程尝试通过CAS操作修改
state(从0→1),如果成功,标记当前线程为锁的持有者。 - 如果失败(说明锁已被占用),检查占用锁的线程是否是自己(可重入判断):如果是,
state递增(重入次数+1);如果不是,当前线程进入等待队列阻塞。
- 线程尝试通过CAS操作修改
- 释放锁(unlock()):
- 线程调用
unlock()时,state递减(重入次数-1)。 - 当
state减为0时,标记锁的持有者为null,并从等待队列中唤醒一个线程来竞争锁。
- 线程调用
- 获取锁(lock()):
-
公平锁与非公平锁:
ReentrantLock默认是非公平锁(构造函数可传入true创建公平锁)。- 非公平锁:新线程尝试获取锁时,可能"插队"(直接竞争锁,而不是排队),效率更高但可能导致线程饥饿。
- 公平锁:新线程必须加入等待队列,按"先来后到"的顺序获取锁,公平但效率较低。
二、ReentrantLock与synchronized的区别
synchronized是Java内置的隐式锁(无需手动释放),依赖JVM实现;而ReentrantLock是JDK提供的工具类,依赖代码实现。两者核心区别如下:
| 维度 | ReentrantLock | synchronized |
|---|---|---|
| 锁的获取/释放 | 显式操作:需手动调用lock()获取,unlock()释放(必须在finally中释放,否则可能死锁) |
隐式操作:进入同步块/方法时自动获取,退出时自动释放(无需手动处理) |
| 可重入性 | 支持(同一线程可多次获取锁,state记录重入次数) |
支持(同一线程可多次进入同步块) |
| 公平性 | 可选择公平/非公平锁(构造函数参数控制) | 仅支持非公平锁(无法保证线程获取锁的顺序) |
| 中断响应 | 支持中断等待锁的线程(lockInterruptibly()方法) |
不支持:等待锁的线程会一直阻塞,无法被中断 |
| 超时获取 | 支持超时获取锁(tryLock(long timeout, TimeUnit unit)),超时后可放弃等待 |
不支持:获取不到锁会一直阻塞 |
| 条件变量(Condition) | 支持多组条件变量(通过newCondition()创建),可精确唤醒特定线程 |
仅支持一组隐式条件变量(通过wait()/notify()/notifyAll()操作),唤醒时随机唤醒一个或全部 |
| 性能 | 高并发场景下性能更稳定(JDK 5后优化明显) | JDK 6后引入偏向锁、轻量级锁等优化,低并发场景性能优异,高并发下略逊于ReentrantLock |
| 使用复杂度 | 较高(需手动释放,否则易死锁) | 较低(无需手动管理,更安全) |
三、总结
- synchronized:适合简单的同步场景,使用方便、不易出错,是Java官方推荐的首选方案(尤其JDK 6后的优化使其性能大幅提升)。
- ReentrantLock:适合需要更灵活控制的场景(如公平锁、超时中断、多条件变量等),但需注意手动释放锁,否则可能导致死锁。
作为初学者,建议先掌握synchronized的使用,理解其基本原理后,再深入学习ReentrantLock和AQS的实现,从而更好地理解Java并发机制的设计思想。
更多推荐
所有评论(0)