Lock锁
核心概念
-
Lock 接口
- 定义锁的基本操作:
lock()
、unlock()
、tryLock()
等。 - 替代
synchronized
的显式锁机制,支持更细粒度的控制。
- 定义锁的基本操作:
-
ReentrantLock(可重入锁)
- 最常见的
Lock
实现类,允许同一线程多次获取同一把锁(避免死锁)。
- 最常见的
基本用法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockExample {private final Lock lock = new ReentrantLock();private int counter = 0;public void increment() {lock.lock(); // 获取锁try {counter++;} finally {lock.unlock(); // 确保释放锁}}
}
Lock 与 synchronized 的区别
1. 实现方式
-
synchronized
- JVM 内置关键字:通过对象头的锁状态(偏向锁→轻量级锁→重量级锁)实现,由 JVM 自动管理。
- 隐式加锁:无需手动释放锁,代码块或方法执行完毕自动释放。
-
Lock
- Java API 接口(如
ReentrantLock
):基于AQS
(AbstractQueuedSynchronizer)实现,需显式调用lock()
和unlock()
。 - 显式加锁:必须手动释放锁(通常放在
finally
块中),否则可能导致死锁。
- Java API 接口(如
2. 锁特性
特性 | synchronized | Lock |
---|---|---|
公平性 | 仅支持非公平锁 | 支持公平锁和非公平锁(构造参数指定) |
中断响应 | 不支持中断等待线程 | 支持 lockInterruptibly() 中断等待 |
超时机制 | 不支持 | 支持 tryLock(timeout) 设定超时时间 |
条件变量 | 单一条件(wait() /notify() ) | 支持多个 Condition 对象,精细化线程唤醒 |
锁状态查询 | 无法判断锁是否被占用 | 支持 isLocked() 等方法查询锁状态 |
3. 性能差异
- 低竞争场景:
synchronized
性能更优(JVM 的偏向锁、轻量级锁优化减少开销)。 - 高竞争场景:
Lock
性能更好(基于 CAS 自旋减少线程阻塞,支持更细粒度的锁控制)。
4. 锁类型
- 均为悲观锁:
两者都假设并发冲突必然发生,访问共享资源前先加锁(synchronized
直接阻塞,Lock
可能自旋后阻塞)。 - 乐观锁 是另一种机制(如
CAS
或版本号),无需加锁,通过冲突检测实现线程安全。
5. 使用场景
-
优先
synchronized
:- 简单同步需求(如单例模式、简单代码块)。
- 低线程竞争场景(利用 JVM 锁优化)。
-
优先
Lock
:- 需要复杂控制(如超时、中断、公平锁)。
- 高并发场景(减少线程阻塞,提升吞吐量)。
- 需要绑定多个条件变量(如生产者-消费者模型)。
总结
synchronized
:简单、自动管理,适合基础同步需求。Lock
:灵活、功能强大,适合高并发和复杂场景。- 选择依据:优先
synchronized
,复杂需求或性能瓶颈时改用Lock
。
高级功能
-
尝试获取锁 (
tryLock()
)if (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试在1秒内获取锁try {// 临界区代码} finally {lock.unlock();} } else {// 超时处理 }
-
公平锁
Lock fairLock = new ReentrantLock(true); // 公平锁,按等待顺序分配锁
-
条件变量 (
Condition
)Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();// 线程等待条件 lock.lock(); try {condition.await(); // 释放锁并等待,类似 wait() } finally {lock.unlock(); }// 唤醒等待线程 lock.lock(); try {condition.signal(); // 类似 notify() } finally {lock.unlock(); }
读写锁 (ReentrantReadWriteLock)
-
适用于读多写少场景,提高并发性能:
ReadWriteLock rwLock = new ReentrantReadWriteLock(); Lock readLock = rwLock.readLock(); // 读锁(共享) Lock writeLock = rwLock.writeLock(); // 写锁(独占)public void readData() {readLock.lock();try {// 读操作} finally {readLock.unlock();} }public void writeData() {writeLock.lock();try {// 写操作} finally {writeLock.unlock();} }
注意事项
- 必须在
finally
中释放锁:避免因异常导致锁无法释放。 - 避免死锁:确保锁的获取和释放顺序一致。
- 性能考量:高竞争场景下,
synchronized
经过 JVM 优化后性能接近Lock
,但Lock
提供更多控制选项。
适用场景
- 需要细粒度控制锁(如超时、可中断)。
- 需要公平锁或读写锁。
- 需要多个条件变量实现复杂线程协作。
通过合理使用 Lock
,可以显著提升多线程程序的灵活性和性能。