核心概念
-
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,可以显著提升多线程程序的灵活性和性能。