核心概念
-
Lock 接口
- 定义锁的基本操作:
lock()
、unlock()
、tryLock()
等。 - 替代
synchronized
的显式锁机制,支持更细粒度的控制。
- 定义锁的基本操作:
-
ReentrantLock(可重入锁)
- 最常见的
Lock
实现类,允许同一线程多次获取同一把锁(避免死锁)。
- 最常见的
基本用法
java
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()
)javaif (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试在1秒内获取锁 try { // 临界区代码 } finally { lock.unlock(); } } else { // 超时处理 }
-
公平锁
javaLock fairLock = new ReentrantLock(true); // 公平锁,按等待顺序分配锁
-
条件变量 (
Condition
)javaLock 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)
-
适用于读多写少场景,提高并发性能:
javaReadWriteLock 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
,可以显著提升多线程程序的灵活性和性能。