JDK 8 Semaphore 源码详解(详细注释版)
1. 类定义和基本属性
public class Semaphore implements java.io.Serializable {
// 序列化版本号
private static final long serialVersionUID = -322257861600715216L;
/**
* Semaphore的核心同步器
* 继承自AbstractQueuedSynchronizer(AQS)
* 通过AQS的state字段来表示可用的许可数量
*
* 设计巧妙之处:
* - 利用AQS的共享锁机制实现信号量
* - state字段表示可用许可数
* - 正数表示可用许可,0表示无可用许可
* - 通过共享模式实现多个线程同时获取许可
*/
private final Sync sync;
/**
* 同步器抽象基类
* 继承自AbstractQueuedSynchronizer
* 提供了信号量的基本功能
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
/**
* 构造方法
* 初始化信号量的许可数量
* @param permits 初始许可数量
*
* 初始化过程:
* 1. 调用AQS的setState方法设置state字段
* 2. state字段表示可用的许可数量
* 3. permits必须为非负数
*/
Sync(int permits) {
setState(permits);
}
/**
* 获取当前可用的许可数量
* @return 当前可用的许可数量
*
* 获取过程:
* 1. 调用AQS的getState方法获取state字段
* 2. state字段表示可用的许可数量
* 3. 返回当前可用许可数
*
* 时间复杂度:O(1)
* 线程安全性:完全线程安全
*/
final int getPermits() {
return getState();
}
/**
* 非公平方式获取许可
* 这是Semaphore的核心实现
* @param acquires 要获取的许可数量
* @return 如果获取成功返回true,否则返回false
*
* 获取过程:
* 1. 使用CAS操作尝试减少许可数量
* 2. 如果成功,获取成功返回true
* 3. 如果失败,说明许可不足,返回false
*
* 非公平特点:
* - 允许插队,不检查等待队列
* - 性能更好,但可能导致线程饥饿
*/
final boolean nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState(); // 获取当前可用许可数
int remaining = available - acquires; // 计算剩余许可数
// 如果剩余许可数为负或CAS更新成功
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining >= 0; // 返回是否获取成功
}
}
/**
* 释放许可
* @param releases 要释放的许可数量
* @return 总是返回true(共享模式)
*
* 释放过程:
* 1. 增加许可数量
* 2. 唤醒等待队列中的线程
* 3. 返回true表示需要唤醒等待线程
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*/
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState(); // 获取当前许可数
int next = current + releases; // 计算新的许可数
// 检查是否溢出
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
// CAS操作更新许可数
if (compareAndSetState(current, next))
return true; // 返回true表示需要唤醒等待线程
}
}
/**
* 减少许可数量(不阻塞)
* 这是一个特殊的操作,用于减少许可数量而不获取许可
* @param reductions 要减少的许可数量
*
* 减少过程:
* 1. 减少许可数量
* 2. 使用CAS操作保证原子性
* 3. 如果减少后许可数为负,抛出异常
*
* 使用场景:
* - 动态调整信号量的容量
* - 实现资源的动态缩减
* - 模拟资源的永久性减少
*/
final void reducePermits(int reductions) {
for (;;) {
int current = getState(); // 获取当前许可数
int next = current - reductions; // 计算新的许可数
// 检查参数有效性
if (next > current) // underflow
throw new Error("Permit count underflow");
// CAS操作更新许可数
if (compareAndSetState(current, next))
return;
}
}
/**
* 获取并清空所有许可
* 这是一个特殊的操作,用于获取所有可用许可并将许可数清零
* @return 获取到的许可数量
*
* 获取过程:
* 1. 获取当前所有可用许可
* 2. 将许可数清零
* 3. 返回获取到的许可数量
*
* 使用场景:
* - 实现紧急模式
* - 清空信号量状态
* - 实现特殊的安全机制
*/
final int drainPermits() {
for (;;) {
int current = getState(); // 获取当前许可数
// 如果没有许可可用,返回0
if (current == 0 || compareAndSetState(current, 0))
return current; // 返回获取到的许可数量
}
}
}
/**
* 非公平同步器实现
* 允许插队,性能更好但可能导致线程饥饿
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
/**
* 构造方法
* @param permits 初始许可数量
*/
NonfairSync(int permits) {
super(permits);
}
/**
* 尝试获取共享锁(非公平)
* @param acquires 要获取的许可数量
* @return 如果获取成功返回正数,否则返回负数
*
* 获取过程:
* 1. 调用非公平获取方法
* 2. 返回获取结果
*/
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
/**
* 公平同步器实现
* 按照等待顺序获取许可,避免线程饥饿
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
/**
* 构造方法
* @param permits 初始许可数量
*/
FairSync(int permits) {
super(permits);
}
/**
* 尝试获取共享锁(公平)
* @param acquires 要获取的许可数量
* @return 如果获取成功返回正数,否则返回负数
*
* 公平获取过程:
* 1. 检查是否有前驱节点(确保公平性)
* 2. 如果没有前驱节点,调用非公平获取方法
* 3. 返回获取结果
*/
protected int tryAcquireShared(int acquires) {
for (;;) {
// hasQueuedPredecessors()检查是否有前驱节点
if (hasQueuedPredecessors())
return -1; // 有前驱节点,获取失败
// 调用非公平获取方法
if (nonfairTryAcquireShared(acquires))
return 1; // 获取成功
return -1; // 获取失败
}
}
}
2. 构造方法(详细注释)
/**
* 默认构造方法(非公平)
* 创建一个指定许可数量的Semaphore
* @param permits 初始许可数量
*
* 初始化过程:
* 1. 验证permit参数的有效性
* 2. 创建NonfairSync实例
* 3. 初始化许可数量
*
* 参数说明:
* - permits必须为非负数
* - permits=0表示初始时没有可用许可
* - permits>0表示初始时有指定数量的可用许可
*
* @throws IllegalArgumentException 如果permits为负数
*/
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/**
* 指定公平策略的构造方法
* @param permits 初始许可数量
* @param fair 是否使用公平策略
*
* 公平性说明:
* - fair=true:使用公平锁,按照请求顺序获取许可
* - fair=false:使用非公平锁,允许插队,性能更好
*
* 选择建议:
* - 高并发场景:使用非公平锁(默认)
* - 需要避免饥饿:使用公平锁
* - 对性能要求极高:使用非公平锁
*
* @throws IllegalArgumentException 如果permits为负数
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
3. 核心获取方法(详细注释)
/**
* 获取一个许可
* 如果没有可用许可,当前线程会阻塞直到获取许可
*
* 获取过程:
* 1. 调用AQS的acquireSharedInterruptibly方法
* 2. 如果有可用许可,立即获取
* 3. 如果没有可用许可,加入等待队列并阻塞
* 4. 被唤醒后重新尝试获取许可
* 5. 重复步骤2-4直到获取许可
*
* 中断处理:
* - 如果线程在等待过程中被中断,会抛出InterruptedException
* - 线程可以被优雅地中止
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(等待时间)
* 线程安全性:完全线程安全
*
* @throws InterruptedException 如果线程被中断
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1); // 调用AQS的可中断共享获取方法
}
/**
* 获取指定数量的许可
* 如果没有足够的可用许可,当前线程会阻塞直到获取足够的许可
*
* 获取过程:
* 1. 调用AQS的acquireSharedInterruptibly方法
* 2. 如果有足够的可用许可,立即获取
* 3. 如果没有足够的可用许可,加入等待队列并阻塞
* 4. 被唤醒后重新尝试获取许可
* 5. 重复步骤2-4直到获取足够的许可
*
* 参数说明:
* - permits要获取的许可数量
* - permits必须为正数
*
* 中断处理:
* - 如果线程在等待过程中被中断,会抛出InterruptedException
* - 线程可以被优雅地中止
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(等待时间)
* 线程安全性:完全线程安全
*
* @param permits 要获取的许可数量
* @throws InterruptedException 如果线程被中断
* @throws IllegalArgumentException 如果permits为负数
*/
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits); // 调用AQS的可中断共享获取方法
}
/**
* 获取一个许可(不可中断)
* 如果没有可用许可,当前线程会阻塞直到获取许可
*
* 与acquire()的区别:
* - acquire()可被中断
* - acquireUninterruptibly()不可被中断
*
* 获取过程:
* 1. 调用AQS的acquireShared方法
* 2. 如果有可用许可,立即获取
* 3. 如果没有可用许可,加入等待队列并阻塞
* 4. 被唤醒后重新尝试获取许可
* 5. 重复步骤2-4直到获取许可
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(等待时间)
* 线程安全性:完全线程安全
*/
public void acquireUninterruptibly() {
sync.acquireShared(1); // 调用AQS的共享获取方法
}
/**
* 获取指定数量的许可(不可中断)
* 如果没有足够的可用许可,当前线程会阻塞直到获取足够的许可
*
* 与acquire(int permits)的区别:
* - acquire(int permits)可被中断
* - acquireUninterruptibly(int permits)不可被中断
*
* 获取过程:
* 1. 调用AQS的acquireShared方法
* 2. 如果有足够的可用许可,立即获取
* 3. 如果没有足够的可用许可,加入等待队列并阻塞
* 4. 被唤醒后重新尝试获取许可
* 5. 重复步骤2-4直到获取足够的许可
*
* 参数说明:
* - permits要获取的许可数量
* - permits必须为正数
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(等待时间)
* 线程安全性:完全线程安全
*
* @param permits 要获取的许可数量
* @throws IllegalArgumentException 如果permits为负数
*/
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits); // 调用AQS的共享获取方法
}
/**
* 尝试获取一个许可(非阻塞)
* 立即返回,不会阻塞当前线程
*
* 获取结果:
* - 如果有可用许可,立即获取并返回true
* - 如果没有可用许可,立即返回false
*
* 使用场景:
* - 避免线程阻塞
* - 实现超时控制
* - 检查许可状态
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*
* @return 如果获取成功返回true,否则返回false
*/
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
/**
* 尝试获取指定数量的许可(非阻塞)
* 立即返回,不会阻塞当前线程
*
* 获取结果:
* - 如果有足够的可用许可,立即获取并返回true
* - 如果没有足够的可用许可,立即返回false
*
* 使用场景:
* - 避免线程阻塞
* - 实现超时控制
* - 检查许可状态
*
* 参数说明:
* - permits要获取的许可数量
* - permits必须为正数
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*
* @param permits 要获取的许可数量
* @return 如果获取成功返回true,否则返回false
* @throws IllegalArgumentException 如果permits为负数
*/
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
/**
* 尝试获取一个许可(带超时)
* 如果没有可用许可,等待指定时间
*
* 超时处理:
* - 在指定时间内获取许可,返回true
* - 超时未获取到许可,返回false
* - 等待过程中可被中断
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(等待时间)
* 线程安全性:完全原子性
*
* @param timeout 等待时间
* @param unit 时间单位
* @return 如果获取成功返回true,否则返回false
* @throws InterruptedException 如果线程被中断
*/
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* 尝试获取指定数量的许可(带超时)
* 如果没有足够的可用许可,等待指定时间
*
* 超时处理:
* - 在指定时间内获取足够的许可,返回true
* - 超时未获取到足够的许可,返回false
* - 等待过程中可被中断
*
* 参数说明:
* - permits要获取的许可数量
* - permits必须为正数
* - timeout等待时间
* - unit时间单位
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(等待时间)
* 线程安全性:完全原子性
*
* @param permits 要获取的许可数量
* @param timeout 等待时间
* @param unit 时间单位
* @return 如果获取成功返回true,否则返回false
* @throws InterruptedException 如果线程被中断
* @throws IllegalArgumentException 如果permits为负数
*/
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
4. 核心释放方法(详细注释)
/**
* 释放一个许可
* 将一个许可返回给信号量,增加可用许可数量
*
* 释放过程:
* 1. 调用AQS的releaseShared方法
* 2. 增加许可数量
* 3. 唤醒等待队列中的线程
* 4. 允许等待线程获取许可
*
* 注意事项:
* - 可以释放比初始许可数更多的许可
* - 释放的许可可以被其他线程获取
* - 没有限制哪个线程可以释放许可
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*/
public void release() {
sync.releaseShared(1); // 调用AQS的共享释放方法
}
/**
* 释放指定数量的许可
* 将指定数量的许可返回给信号量,增加可用许可数量
*
* 释放过程:
* 1. 调用AQS的releaseShared方法
* 2. 增加指定数量的许可
* 3. 唤醒等待队列中的线程
* 4. 允许等待线程获取许可
*
* 参数说明:
* - permits要释放的许可数量
* - permits必须为正数
*
* 注意事项:
* - 可以释放比初始许可数更多的许可
* - 释放的许可可以被其他线程获取
* - 没有限制哪个线程可以释放许可
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*
* @param permits 要释放的许可数量
* @throws IllegalArgumentException 如果permits为负数
*/
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits); // 调用AQS的共享释放方法
}
5. 查询和控制方法(详细注释)
/**
* 获取当前可用的许可数量
* 返回当前信号量中可用的许可数量
*
* 查询过程:
* 1. 调用sync的getPermits方法
* 2. 获取AQS的state字段值
* 3. 返回当前可用许可数
*
* 注意事项:
* - 这是一个瞬时值,可能很快过时
* - 在高并发环境下可能不是准确值
* - 主要用于调试和监控
*
* 时间复杂度:O(1)
* 线程安全性:完全线程安全
*
* @return 当前可用的许可数量
*/
public int availablePermits() {
return sync.getPermits();
}
/**
* 获取并清空所有许可
* 获取当前所有可用的许可并将许可数清零
*
* 获取过程:
* 1. 调用sync的drainPermits方法
* 2. 获取当前所有可用许可
* 3. 将许可数清零
* 4. 返回获取到的许可数量
*
* 使用场景:
* - 实现紧急模式
* - 清空信号量状态
* - 实现特殊的安全机制
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*
* @return 获取到的许可数量
*/
public int drainPermits() {
return sync.drainPermits();
}
/**
* 减少许可数量
* 减少指定数量的许可,不获取许可
*
* 减少过程:
* 1. 调用sync的reducePermits方法
* 2. 减少指定数量的许可
* 3. 使用CAS操作保证原子性
* 4. 如果减少后许可数为负,抛出异常
*
* 使用场景:
* - 动态调整信号量的容量
* - 实现资源的动态缩减
* - 模拟资源的永久性减少
*
* 参数说明:
* - reduction要减少的许可数量
* - reduction必须为非负数
*
* 时间复杂度:O(1) 平均情况,O(∞) 最坏情况(高竞争)
* 线程安全性:完全原子性
*
* @param reduction 要减少的许可数量
* @throws IllegalArgumentException 如果reduction为负数
*/
protected void reducePermits(int reduction) {
if (reduction < 0) throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
/**
* 判断是否有线程正在等待获取许可
* 检查AQS的等待队列中是否有线程
*
* 查询结果:
* - 如果有等待线程返回true
* - 如果没有等待线程返回false
*
* 使用场景:
* - 监控信号量的使用情况
* - 实现负载监控
* - 性能调优
*
* 时间复杂度:O(1)
* 线程安全性:完全线程安全
*
* @return 如果有等待线程返回true,否则返回false
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* 获取等待获取许可的线程数量
* 返回AQS等待队列中的线程数量
*
* 查询结果:
* - 返回等待队列中的线程数量
* - 这是一个估算值,因为线程状态可能随时变化
*
* 使用场景:
* - 监控信号量的竞争程度
* - 实现负载监控
* - 性能调优
*
* 时间复杂度:O(1)
* 线程安全性:完全线程安全
*
* @return 等待线程数量
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* 获取等待获取许可的线程集合
* 返回AQS等待队列中线程的集合
*
* 查询结果:
* - 返回等待队列中线程的集合
* - 集合是线程安全的快照
*
* 使用场景:
* - 调试和诊断
* - 实现管理工具
* - 分析系统状态
*
* 时间复杂度:O(n) n为等待线程数
* 线程安全性:完全线程安全
*
* @return 等待线程集合
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
6. ReentrantReadWriteLock 的特点分析
核心设计理念:
/**
* ReentrantReadWriteLock的核心设计思想:
*
* 1. 读写分离:
* - 读锁:共享锁,多个读线程可以同时持有
* - 写锁:独占锁,写线程独占访问
* - 读读不互斥,读写互斥,写写互斥
*
* 2. 可重入性:
* - 支持读锁和写锁的可重入
* - 每个线程维护自己的持有计数
* - 完全释放时才允许其他线程获取
*
* 3. 锁降级:
* - 支持写锁降级为读锁
* - 先获取写锁,再获取读锁,最后释放写锁
* - 保证数据的一致性
*
* 4. 公平性选择:
* - 提供公平和非公平两种模式
* - 公平锁避免线程饥饿
* - 非公平锁提高性能
*
* 5. 线程本地存储:
* - 使用ThreadLocal跟踪每个线程的读锁持有情况
* - 避免竞争和同步开销
* - 优化单个读线程的场景
*
* 6. AQS集成:
* - 继承AbstractQueuedSynchronizer
* - 复用成熟的队列管理机制
* - 提供完整的同步框架
*
* 适用场景:
* - 读操作远多于写操作的场景
* - 需要高并发读取的场景
* - 需要比ReentrantLock更细粒度控制的场景
* - 需要Condition支持的写锁场景
*/
性能特征分析:
/**
* ReentrantReadWriteLock的性能特征:
*
* 获取锁时间复杂度:
* - 无竞争读锁:O(1) - 直接CAS操作
* - 无竞争写锁:O(1) - 直接CAS操作
* - 有竞争:O(1) 平均,O(n) 最坏(等待队列长度)
*
* 释放锁时间复杂度:
* - 读锁释放:O(1) - 更新状态并可能唤醒写锁
* - 写锁释放:O(1) - 更新状态并唤醒等待线程
*
* 内存复杂度:
* - O(1) 基本开销
* - O(n) 等待队列开销(n为等待线程数)
* - O(t) ThreadLocal开销(t为持有读锁的线程数)
*
* 并发特性:
* - 完全线程安全
* - 支持高并发读取
* - 读写互斥保证数据一致性
* - 无锁设计(通过CAS和AQS)
*
* 读写性能对比:
* - 读锁:多个线程可以并发获取
* - 写锁:独占访问,性能相对较低
* - 读多写少:性能优势明显
* - 写多读少:可能不如ReentrantLock
*
* 公平性对比:
* - 非公平锁:吞吐量更高,可能有线程饥饿
* - 公平锁:避免饥饿,吞吐量略低
*
* 与ReentrantLock对比:
* - 读写分离:更细粒度的控制
* - 读并发:支持多个读线程同时访问
* - 复杂度:实现更复杂,开销略大
* - 适用场景:读多写少的场景
*/
7. 使用示例和最佳实践
/**
* 使用示例:
*
* // 基本使用
* ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
* Lock readLock = rwLock.readLock();
* Lock writeLock = rwLock.writeLock();
*
* // 读操作
* readLock.lock();
* try {
* // 读取共享数据
* // 多个读线程可以同时执行
* } finally {
* readLock.unlock();
* }
*
* // 写操作
* writeLock.lock();
* try {
* // 修改共享数据
* // 独占访问,其他读写操作都会阻塞
* } finally {
* writeLock.unlock();
* }
*
* // 可中断的锁获取
* try {
* readLock.lockInterruptibly();
* try {
* // 读操作
* } finally {
* readLock.unlock();
* }
* } catch (InterruptedException e) {
* Thread.currentThread().interrupt();
* }
*
* // 带超时的锁获取
* try {
* if (writeLock.tryLock(5, TimeUnit.SECONDS)) {
* try {
* // 写操作
* } finally {
* writeLock.unlock();
* }
* } else {
* // 获取锁超时
* System.out.println("Failed to acquire write lock within timeout");
* }
* } catch (InterruptedException e) {
* Thread.currentThread().interrupt();
* }
*
* // 公平锁
* ReentrantReadWriteLock fairRwLock = new ReentrantReadWriteLock(true);
*
* // 使用Condition(仅写锁支持)
* Condition condition = writeLock.newCondition();
*
* // 等待条件
* writeLock.lock();
* try {
* while (!conditionMet()) {
* condition.await();
* }
* // 条件满足后的处理
* } finally {
* writeLock.unlock();
* }
*
* // 通知等待的线程
* writeLock.lock();
* try {
* // 改变条件
* setConditionMet(true);
* condition.signal(); // 或者 condition.signalAll();
* } finally {
* writeLock.unlock();
* }
*
* 最佳实践:
*
* 1. 正确的锁获取和释放:
* ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
* Lock readLock = rwLock.readLock();
* Lock writeLock = rwLock.writeLock();
*
* // 读操作
* readLock.lock();
* try {
* // 读取操作
* } finally {
* readLock.unlock(); // 必须在finally块中释放
* }
*
* // 写操作
* writeLock.lock();
* try {
* // 写入操作
* } finally {
* writeLock.unlock(); // 必须在finally块中释放
* }
*
* 2. 避免读写锁嵌套:
* // 错误的做法:可能导致死锁
* readLock.lock();
* try {
* // 一些操作
* if (needToUpdate()) {
* writeLock.lock(); // 危险!可能导致死锁
* try {
* // 更新操作
* } finally {
* writeLock.unlock();
* }
* }
* } finally {
* readLock.unlock();
* }
*
* // 正确的做法:统一使用写锁
* writeLock.lock();
* try {
* // 读取操作
* // 更新操作
* } finally {
* writeLock.unlock();
* }
*
* 3. 合理使用可重入性:
* private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
* private final Lock readLock = rwLock.readLock();
* private final Lock writeLock = rwLock.writeLock();
*
* public void outerReadMethod() {
* readLock.lock();
* try {
* innerReadMethod(); // 同一线程可以再次获取读锁
* } finally {
* readLock.unlock(); // 只需要一次unlock
* }
* }
*
* public void innerReadMethod() {
* readLock.lock();
* try {
* // 读取操作
* System.out.println("Read hold count: " + rwLock.getReadHoldCount());
* } finally {
* readLock.unlock();
* }
* }
*
* 4. 选择合适的公平性:
* // 读多写少且对公平性要求不高:非公平锁(默认)
* ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
*
* // 需要避免饥饿:公平锁
* ReentrantReadWriteLock fairRwLock = new ReentrantReadWriteLock(true);
*
* 5. 设置合理的超时时间:
* try {
* if (readLock.tryLock(10, TimeUnit.SECONDS)) {
* try {
* // 读操作
* } finally {
* readLock.unlock();
* }
* } else {
* // 超时处理
* System.out.println("Read operation timed out");
* }
* } catch (InterruptedException e) {
* Thread.currentThread().interrupt();
* }
*
* 6. 监控锁的使用情况:
* ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
*
* // 检查锁的状态
* if (rwLock.isWriteLocked()) {
* System.out.println("Write lock is currently held");
* }
*
* if (rwLock.isWriteLockedByCurrentThread()) {
* System.out.println("Current thread holds the write lock");
* System.out.println("Write hold count: " + rwLock.getWriteHoldCount());
* }
*
* System.out.println("Read lock count: " + rwLock.getReadLockCount());
* System.out.println("Current thread read hold count: " + rwLock.getReadHoldCount());
*
* // 检查等待队列
* if (rwLock.hasQueuedThreads()) {
* System.out.println("Threads waiting for lock: " + rwLock.getQueueLength());
* }
*
* 7. 使用Condition(仅写锁支持):
* private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
* private final Lock writeLock = rwLock.writeLock();
* private final Condition notFull = writeLock.newCondition();
* private final Condition notEmpty = writeLock.newCondition();
*
* // 生产者
* public void put(Object item) throws InterruptedException {
* writeLock.lock();
* try {
* while (isFull()) {
* notFull.await(); // 等待空间
* }
* // 添加元素
* notEmpty.signal(); // 通知消费者
* } finally {
* writeLock.unlock();
* }
* }
*
* // 消费者
* public Object take() throws InterruptedException {
* writeLock.lock();
* try {
* while (isEmpty()) {
* notEmpty.await(); // 等待元素
* }
* Object item = remove(); // 移除元素
* notFull.signal(); // 通知生产者
* return item;
* } finally {
* writeLock.unlock();
* }
* }
*
* 8. 避免长时间持有锁:
* // 错误的做法:长时间持有锁
* writeLock.lock();
* try {
* // 大量计算或I/O操作
* expensiveOperation(); // 不应该在锁内执行
* // 更新共享数据
* } finally {
* writeLock.unlock();
* }
*
* // 正确的做法:减少锁持有时间
* Object result = expensiveOperation(); // 在锁外执行
* writeLock.lock();
* try {
* // 只在必要时持有锁
* updateSharedData(result);
* } finally {
* writeLock.unlock();
* }
*
* 9. 处理异常:
* ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
* Lock readLock = rwLock.readLock();
*
* readLock.lock();
* try {
* // 可能抛出异常的代码
* riskyReadOperation();
* } catch (Exception e) {
* // 处理异常
* System.err.println("Read operation failed: " + e.getMessage());
* throw e; // 重新抛出或处理
* } finally {
* readLock.unlock(); // 确保锁被释放
* }
*
* 10. 性能调优:
* // 在高并发读场景下监控性能
* ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
*
* // 定期检查锁的使用情况
* if (rwLock.getReadLockCount() > 1000) {
* System.out.println("High read contention detected");
* // 可能需要优化数据结构或访问模式
* }
*
* if (rwLock.getQueueLength() > 100) {
* System.out.println("High lock contention detected");
* // 可能需要减少锁的粒度
* }
*/
8. 与其他同步机制的比较
/**
* ReentrantReadWriteLock vs ReentrantLock vs synchronized vs StampedLock:
*
* ReentrantReadWriteLock:
* - 读写分离,读读不互斥
* - 支持多个读线程并发访问
* - 写锁独占访问
* - 支持公平性和非公平性
* - 读锁不支持Condition
* - 写锁支持Condition
*
* ReentrantLock:
* - 独占锁,读写都互斥
* - 不区分读写操作
* - 支持公平性和非公平性
* - 支持Condition
* - 实现相对简单
*
* synchronized:
* - JVM内置锁
* - 自动释放锁
* - JVM优化(偏向锁、轻量级锁等)
* - 不支持读写分离
* - 不支持公平性选择
* - 不支持Condition(Object的wait/notify)
*
* StampedLock:
* - JDK 8新增
* - 更高性能的读写锁
* - 支持乐观读
* - 不支持重入
* - 不支持Condition
* - 更复杂的使用方式
*
* 选择建议:
* - 读多写少:ReentrantReadWriteLock
* - 简单同步需求:synchronized
* - 需要Condition:ReentrantLock
* - 高性能读操作:StampedLock
* - 复杂同步逻辑:ReentrantLock
*
* 性能对比:
* - 无竞争:synchronized ≈ ReentrantLock ≈ ReentrantReadWriteLock
* - 有竞争:ReentrantLock ≥ synchronized(JDK 1.6后)
* - 读多写少:ReentrantReadWriteLock > ReentrantLock > synchronized
* - 高并发读:StampedLock > ReentrantReadWriteLock
* - 写多读少:ReentrantLock ≥ ReentrantReadWriteLock
*/
9. 总结
ReentrantReadWriteLock 的核心特性:
-
读写分离:
- 读锁:共享锁,多个读线程可以同时持有
- 写锁:独占锁,写线程独占访问
- 读读不互斥,读写互斥,写写互斥
-
可重入性:
- 支持读锁和写锁的可重入
- 每个线程维护自己的持有计数
- 完全释放时才允许其他线程获取
-
锁降级:
- 支持写锁降级为读锁
- 先获取写锁,再获取读锁,最后释放写锁
- 保证数据的一致性
-
公平性选择:
- 提供公平和非公平两种模式
- 公平锁避免线程饥饿
- 非公平锁提高性能
-
线程本地存储:
- 使用ThreadLocal跟踪每个线程的读锁持有情况
- 避免竞争和同步开销
- 优化单个读线程的场景
-
AQS集成:
- 继承AbstractQueuedSynchronizer
- 复用成熟的队列管理机制
- 提供完整的同步框架
-
Condition支持:
- 写锁支持Condition
- 提供灵活的线程等待/通知机制
- 不支持读锁的Condition
-
丰富的查询API:
- 提供详细的锁状态查询
- 支持监控和诊断
- 便于调试和性能调优
适用场景:
- 读操作远多于写操作的场景
- 需要高并发读取的场景
- 需要比ReentrantLock更细粒度控制的场景
- 需要Condition支持的写锁场景
- 缓存系统、数据库访问等读多写少的应用
注意事项:
- 必须在finally块中释放锁
- 避免读写锁嵌套导致死锁
- 合理选择公平性策略
- 正确处理中断异常
- 监控锁的使用情况
- 避免长时间持有锁
- 注意读写锁的语义区别
性能优化建议:
- 根据读写比例选择合适的锁类型
- 合理设置超时时间
- 使用公平锁避免线程饥饿
- 监控锁的竞争情况
- 优化临界区代码减少持有时间
- 考虑使用更细粒度的锁
- 定期分析和调优锁的使用
- 在读多写少的场景下充分发挥读并发优势

166

被折叠的 条评论
为什么被折叠?



