下文笔者讲述java并发编程中,锁的选择简介说明,如下所示
通过本篇文章的学习,你可以彻底掌握不同的场景选择不同的锁
如下所示
常见锁类型及其特点
锁类型 | 所属包 | 是否内置 | 可中断 | 可超时 | 公平性 | 支持条件变量 | 适用场景 |
`synchronized` 关键字 | JVM 内置 | ✅ | ❌ | ❌ | ❌ | ❌ | 方法/代码块同步,简单并发控制 |
`ReentrantLock` | `java.util.concurrent.locks` | ❌ | ✅ | ✅ | ✅(可配置) | ✅ | 需要灵活控制的同步场景 |
`ReentrantReadWriteLock` | `java.util.concurrent.locks` | ❌ | ✅ | ✅ | ✅(可配置) | ✅ | 读多写少的共享资源控制 |
`StampedLock` | `java.util.concurrent.locks` | ❌ | ✅(部分支持) | ✅(部分支持) | ❌ | ❌ | 高性能读多写少场景 |
`Semaphore` | `java.util.concurrent` | ❌ | ✅ | ✅ | ✅(可配置) | ❌ | 资源访问控制、限流 |
`CountDownLatch` | `java.util.concurrent` | ❌ | ❌ | ❌ | ❌ | ❌ | 多线程协作完成任务 |
`CyclicBarrier` | `java.util.concurrent` | ❌ | ❌ | ❌ | ❌ | ❌ | 多线程协同执行阶段任务 |
各种场景选择锁的示例说明
场景一:方法或代码块同步(简单并发)
- 推荐锁:`synchronized`
- 优点:
- 简单易用,JVM 自动加锁解锁。
- 性能优化好(偏向锁、轻量级锁)
-例
public synchronized void doSomething() {
// 同步方法体
}
场景二:需要尝试获取锁、设置超时、公平锁等高级功能
- 推荐锁:`ReentrantLock`
- 优点:
- 支持尝试锁 (`tryLock`)、超时、公平锁。
- 更细粒度控制,适合复杂并发逻辑。
- 例:
ReentrantLock lock = new ReentrantLock(true); // 公平锁
lock.lock();
try {
// 临界区操作
} finally {
lock.unlock();
}
场景三:读多写少(如缓存、配置管理)
- 推荐锁:`ReentrantReadWriteLock` 或 `StampedLock`
- 区别:
- `ReentrantReadWriteLock`:适合写频率不高的场景,读锁共享,写锁独占。
- `StampedLock`:提供乐观读锁,适合高并发读场景,性能更高。
- 例(ReentrantReadWriteLock):
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock();
try {
// 读取数据
} finally {
rwLock.readLock().unlock();
}
场景四:高性能读多写少 + 乐观锁机制
-推荐锁:`StampedLock`
-优点:
- 支持乐观读锁(不阻塞写线程)。
- 性能优于 `ReentrantReadWriteLock`。
- 注意:需手动验证戳记是否有效。
- 例:
StampedLock stampedLock = new StampedLock();
long stamp = stampedLock.tryOptimisticRead();
// 读取数据
if (!stampedLock.validate(stamp)) {
stamp = stampedLock.readLock();
try {
// 重新读取数据
} finally {
stampedLock.unlockRead(stamp);
}
}
场景五:线程协作(如生产者-消费者模型)
- 推荐锁:`ReentrantLock` + `Condition`
- 优点:
- 支持多个等待队列。
- 可实现精确唤醒特定线程。
- 例:
ReentrantLock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 等待生产
}
// 消费数据
} finally {
lock.unlock();
}
场景六:控制资源访问数量(如连接池、限流)
- 推荐锁:`Semaphore`
- 优点:
- 控制同时访问的线程数量。
- 可用于信号量、资源池、限流器。
-例:
Semaphore semaphore = new Semaphore(3); // 最多允许 3 个线程同时访问
semaphore.acquire();
try {
// 访问资源
} finally {
semaphore.release();
}
场景七:多个线程协同执行任务(如并行计算)
- 推荐锁:`CountDownLatch` 或 `CyclicBarrier`
- 区别:
- `CountDownLatch`:计数递减到 0 后不可复用。
- `CyclicBarrier`:可重复使用,所有线程到达屏障后统一继续执行。
-例(CountDownLatch):
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
//执行任务
latch.countDown();
}).start();
}
latch.await(); //主线程等待所有子线程完成
System.out.println("所有任务完成");
各种锁超时、公平、可重入说明
场景 | 推荐锁 | 是否支持中断 | 是否支持超时 | 是否公平 | 是否可重入 |
简单同步 | `synchronized` | ❌ | ❌ | ❌ | ✅ |
高级锁控制 | `ReentrantLock` | ✅ | ✅ | ✅ | ✅ |
读多写少 | `ReentrantReadWriteLock` / `StampedLock` | ✅ | ✅ | ✅ | ✅ |
线程协作 | `ReentrantLock` + `Condition` | ✅ | ✅ | ✅ | ✅ |
限流 / 资源池 | `Semaphore` | ✅ | ✅ | ✅ | ❌ |
多线程协同 | `CountDownLatch` / `CyclicBarrier` | ❌ | ❌ | ❌ | ❌ |