JDK 8 StampedLock 源码详解(详细注释版)
1. 类定义和基本属性
public class StampedLock implements java.io.Serializable {
// 序列化版本号
private static final long serialVersionUID = -6001602636862214147L;
/**
* CPU核心数,用于优化自旋策略
*/
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
* 自旋次数阈值
* 当CPU核心数较少时,使用较小的自旋次数
*/
private static final int SPINS = (NCPU > 1) ? 1 << 8 : 0;
/**
* 阻塞前的自旋次数
* 用于减少不必要的线程阻塞
*/
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/**
* 最大重试次数
* 避免无限循环
*/
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/**
* 溢出值
* 用于处理序列号溢出的情况
*/
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
/**
* 读线程的本地随机数
* 用于优化读线程的撤销操作
*/
private static final int LG_READERS = 7;
/**
* 读线程数组的大小
*/
private static final int RNODES = 1 << LG_READERS;
/**
* 锁的状态字段位数定义
*
* 状态字段(state)的结构:
* - 高位:序列号(stamp),用于版本控制
* - 中位:写锁标志和计数
* - 低位:读锁计数
*
* 这种设计支持乐观读、悲观读和写锁三种模式
*/
private static final long ABITS = 0x00000000000003ffL; // 读写锁掩码
private static final long SBITS = 0x0000000000000fffL; // 序列号掩码
private static final long RFULL = 0x00000000000000ffL; // 读锁满值
private static final long WBIT = 0x0000000000000100L; // 写锁位
private static final long RBITS = 0x00000000000000ffL; // 读锁位掩码
/**
* 最大序列号值
*/
private static final long MMASK = 0xffffffffffffff00L; // 最大掩码
/**
* 序列号增量
*/
private static final long ORIGIN = WBIT << 1; // 初始序列号
/**
* 读线程节点类
* 用于跟踪读线程的状态
*/
static final class WNode {
volatile WNode prev; // 前驱节点
volatile WNode next; // 后继节点
volatile WNode cowait; // 读线程等待队列
volatile Thread thread; // 等待线程
volatile int status; // 节点状态
final int mode; // 节点模式(读/写)
/**
* 构造方法
* @param m 节点模式
* @param prev 前驱节点
*/
WNode(int m, WNode prev) {
mode = m;
this.prev = prev;
thread = Thread.currentThread();
}
}
/**
* 等待队列头节点
*/
private transient volatile WNode whead;
/**
* 等待队列尾节点
*/
private transient volatile WNode wtail;
/**
* 读线程数组
* 用于优化读锁的获取和释放
*/
private transient volatile long readers[];
/**
* 锁状态字段
* 包含序列号、写锁状态和读锁计数
*/
private transient volatile long state;
/**
* 撤销读锁时的随机数
*/
private transient int readerOverflow;
/**
* 默认构造方法
* 创建一个新的StampedLock实例
*
* 初始化过程:
* 1. 设置初始状态为ORIGIN
* 2. 初始化读线程数组
* 3. 准备好锁的基本功能
*/
public StampedLock() {
state = ORIGIN; // 设置初始序列号
readers = new long[RNODES]; // 初始化读线程数组
}
2. 核心锁操作方法(详细注释)
/**
* 尝试获取写锁(非阻塞)
* 这是StampedLock的核心方法之一
*
* 获取过程:
* 1. 检查当前状态是否允许获取写锁
* 2. 如果允许,原子性地更新状态
* 3. 返回时间戳(stamp),用于后续的释放和转换操作
*
* 返回值:
* - 非0值:获取成功的时间戳
* - 0:获取失败
*
* 时间复杂度:O(1)
*
* @return 时间戳,如果获取失败返回0
*/
public long tryWriteLock() {
long s, next; // s: 当前状态, next: 下一个状态
return ((((s = state) & ABITS) == 0L && // 检查是否有读锁或写锁
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ? // CAS更新状态
next : 0L); // 成功返回新状态,失败返回0
}
/**
* 获取写锁(阻塞)
* 如果无法立即获取,当前线程会阻塞直到获取成功
*
* 获取过程:
* 1. 尝试直接获取写锁
* 2. 如果失败,加入等待队列
* 3. 阻塞当前线程直到被唤醒
* 4. 被唤醒后重新尝试获取写锁
*
* 返回值:
* - 非0值:获取成功的时间戳
*
* @return 时间戳
*/
public long writeLock() {
long s, next;
return (U.compareAndSwapLong(this, STATE, s = state, next = s + WBIT) ?
next : acquireWrite(s, next));
}
/**
* 实际的写锁获取实现
* 当直接CAS失败时调用此方法
* @param s 当前状态
* @param next 下一个状态
* @return 时间戳
*/
private long acquireWrite(long s, long next) {
WNode h;
// 如果队列为空或队列头节点为null,初始化队列
if ((h = whead) == null) {
// 创建哨兵节点
if (U.compareAndSwapObject(this, WHEAD, null, new WNode(WMODE, null)))
h = whead;
else
h = whead;
}
WNode p = wtail; // 获取尾节点
WNode node = new WNode(WMODE, p); // 创建新节点
// 将新节点加入队列尾部
if (p == null || !U.compareAndSwapObject(this, WTAIL, p, node))
return slowAcquire(s, next, node); // 如果失败,调用慢速获取
p.next = node; // 链接节点
// 自旋等待
for (int spins = SPINS;;) {
WNode np, pp;
long ns;
if (((ns = state) & ABITS) == 0L) { // 检查锁是否可用
if (U.compareAndSwapLong(this, STATE, ns, ns + WBIT)) {
node.prev = null;
whead = node;
p.next = null;
return ns + WBIT;
}
} else if (Thread.interrupted())
return cancelWaiter(node, p, true);
else if (spins > 0) {
if (ThreadLocalRandom.current().nextInt() >= 0)
--spins;
} else if ((np = node.prev) != p) {
if (np != null && (pp = p.prev) != null && pp != np)
p.prev = null;
} else {
node.status = WAITING;
if ((np = node.prev) != p)
return cancelWaiter(node, np, false);
if (np != null && U.compareAndSwapInt(np, WSTATUS, 0, WAITING))
U.park(false, 0L);
node.status = 0;
if (Thread.interrupted())
return cancelWaiter(node, p, true);
}
}
}
/**
* 慢速写锁获取
* 处理复杂的等待队列情况
*/
private long slowAcquire(long s, long next, WNode node) {
boolean interrupted = false;
WNode p = wtail;
node.prev = p;
if (p == null) {
whead = node;
} else {
p.next = node;
}
wtail = node;
for (;;) {
WNode pp, c; Thread wt;
if ((p = node.prev) == null) {
if (whead == node) {
if ((s = state) == 0L || (s & ABITS) != 0L)
continue;
if (U.compareAndSwapLong(this, STATE, s, s + WBIT)) {
node.prev = null;
whead = node;
p.next = null;
if (interrupted)
Thread.currentThread().interrupt();
return s + WBIT;
}
}
} else if (Thread.interrupted()) {
interrupted = true;
} else if (p.status < 0) {
if (p != whead && (pp = p.prev) != null && pp.prev != null)
node.prev = pp;
else
U.unpark(p.thread);
} else if (p.status == 0 &&
!U.compareAndSwapInt(p, WSTATUS, 0, WAITING))
;
else if ((wt = node.thread) != null)
U.park(false, 0L);
}
}
/**
* 尝试获取读锁(非阻塞)
*
* 获取过程:
* 1. 检查当前状态是否允许获取读锁
* 2. 如果允许,原子性地更新状态
* 3. 返回时间戳(stamp)
*
* 返回值:
* - 非0值:获取成功的时间戳
* - 0:获取失败
*
* @return 时间戳,如果获取失败返回0
*/
public long tryReadLock() {
for (;;) {
long s, m, next; WNode h;
if ((m = (s = state) & ABITS) == WBIT) // 如果有写锁,返回0
return 0L;
else if (m < RFULL) { // 如果读锁未满
if (U.compareAndSwapLong(this, STATE, s, next = s + 1))
return next;
} else if ((h = whead) == null ||
(h.prev == null && h.next == null && h.cowait == null &&
U.compareAndSwapLong(this, STATE, s,
next = s + (WBIT >>> 1))))
return next & SBITS;
else
return 0L;
}
}
/**
* 获取读锁(阻塞)
*
* 获取过程:
* 1. 尝试直接获取读锁
* 2. 如果失败,加入等待队列
* 3. 阻塞当前线程直到被唤醒
* 4. 被唤醒后重新尝试获取读锁
*
* @return 时间戳
*/
public long readLock() {
long s = state, next; // bypass acquireRead on common uncontended case
return ((whead == null && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + 1)) ?
next : acquireRead(s, next));
}
/**
* 实际的读锁获取实现
*/
private long acquireRead(long s, long next) {
return tryIncReaderOverflow(s);
}
/**
* 增加读锁溢出计数
*/
private long tryIncReaderOverflow(long s) {
// 省略具体实现细节
return 0L;
}
3. 乐观读锁方法(详细注释)
/**
* 尝试乐观读
* 这是StampedLock的独特特性,提供高性能的读操作
*
* 乐观读的特点:
* 1. 不阻塞其他线程
* 2. 不修改锁状态
* 3. 返回当前的序列号(stamp)
* 4. 需要配合validate方法验证数据一致性
*
* 使用场景:
* - 读操作非常频繁
* - 写操作相对较少
* - 可以容忍短暂的不一致
*
* @return 序列号,用于后续验证
*/
public long tryOptimisticRead() {
long s;
return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}
/**
* 验证序列号的有效性
* 检查在乐观读期间是否有写操作发生
*
* 验证过程:
* 1. 比较当前序列号与传入的序列号
* 2. 检查是否有写锁被获取
* 3. 如果序列号一致且无写锁,验证成功
*
* 返回值:
* - true:序列号有效,数据未被修改
* - false:序列号无效,数据可能被修改
*
* @param stamp 要验证的序列号
* @return 如果序列号有效返回true,否则返回false
*/
public boolean validate(long stamp) {
U.loadFence(); // 内存屏障,确保验证的正确性
return (stamp & SBITS) == (state & SBITS);
}
/**
* 乐观读的完整使用模式:
*
* long stamp = lock.tryOptimisticRead();
* // 读取数据
* Object data = readData();
* if (!lock.validate(stamp)) {
* // 验证失败,使用悲观读锁
* stamp = lock.readLock();
* try {
* data = readData(); // 重新读取数据
* } finally {
* lock.unlockRead(stamp);
* }
* }
* // 使用数据
* processData(data);
*/
4. 锁转换方法(详细注释)
/**
* 尝试将写锁转换为读锁
* 这是一个优化操作,避免释放写锁再获取读锁
*
* 转换条件:
* 1. 必须持有有效的写锁
* 2. 当前没有其他等待的写锁
*
* 返回值:
* - 非0值:转换成功的新序列号
* - 0:转换失败
*
* @param stamp 写锁的序列号
* @return 读锁的序列号,如果转换失败返回0
*/
public long tryConvertToReadLock(long stamp) {
long a = stamp & ABITS, m, s, next; WNode h;
// 检查是否持有有效的写锁
if ((s = state) == 0L || (m = s & ABITS) != WBIT || a != m)
return 0L;
else if (m < RFULL) {
// 直接转换:减少写锁计数,增加读锁计数
if (U.compareAndSwapLong(this, STATE, s, next = s - WBIT + 1))
return next;
} else if ((h = whead) == null ||
(h.prev == null && h.next == null && h.cowait == null &&
U.compareAndSwapLong(this, STATE, s,
next = s - WBIT + (WBIT >>> 1))))
return next & SBITS;
return 0L;
}
/**
* 尝试将读锁转换为写锁
*
* 转换条件:
* 1. 必须持有有效的读锁
* 2. 当前只有一个读锁持有者(当前线程)
* 3. 没有其他等待的读锁或写锁
*
* 返回值:
* - 非0值:转换成功的新序列号
* - 0:转换失败
*
* @param stamp 读锁的序列号
* @return 写锁的序列号,如果转换失败返回0
*/
public long tryConvertToWriteLock(long stamp) {
long a = stamp & ABITS, m, s, next;
// 检查是否持有有效的读锁且是唯一的读锁持有者
if ((s = state) == 0L || (m = s & ABITS) == 0L || a != m)
return 0L;
else if (m == 1L) {
// 直接转换:减少读锁计数,增加写锁计数
if (U.compareAndSwapLong(this, STATE, s, next = s - 1L + WBIT))
return next;
} else if (a == 1L) {
// 复杂转换:需要等待其他读锁释放
return tryUnlockWriteRead(s, stamp);
}
return 0L;
}
/**
* 尝试将乐观读转换为写锁
*
* 转换条件:
* 1. 传入的stamp必须是有效的乐观读序列号
* 2. 自上次乐观读以来没有发生写操作
*
* 返回值:
* - 非0值:转换成功的新序列号
* - 0:转换失败
*
* @param stamp 乐观读的序列号
* @return 写锁的序列号,如果转换失败返回0
*/
public long tryConvertToWriteLock(long stamp) {
long a = stamp & ABITS, m, s, next;
// 检查是否是有效的乐观读序列号
if ((s = state) == 0L || (m = s & ABITS) != 0L || a != m)
return 0L;
else if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
return next;
return 0L;
}
/**
* 写锁到读锁的复杂转换
*/
private long tryUnlockWriteRead(long s, long stamp) {
// 省略具体实现细节
return 0L;
}
5. 锁释放方法(详细注释)
/**
* 释放写锁
*
* 释放过程:
* 1. 验证序列号的有效性
* 2. 减少写锁计数
* 3. 更新序列号
* 4. 唤醒等待队列中的线程
*
* @param stamp 写锁的序列号
* @throws IllegalMonitorStateException 如果序列号无效
*/
public void unlockWrite(long stamp) {
WNode h;
// 验证序列号并释放写锁
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
// 更新状态:清除写锁位,增加序列号
state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
// 唤醒等待的线程
if ((h = whead) != null && h.next != null)
release(h);
}
/**
* 释放读锁
*
* 释放过程:
* 1. 验证序列号的有效性
* 2. 减少读锁计数
* 3. 如果是最后一个读锁,更新序列号
* 4. 必要时唤醒等待队列中的线程
*
* @param stamp 读锁的序列号
* @throws IllegalMonitorStateException 如果序列号无效
*/
public void unlockRead(long stamp) {
long s, m; WNode h;
// 验证序列号并释放读锁
if (((s = state) & SBITS) != (stamp & SBITS) ||
(stamp & ABITS) == 0L || (m = s & ABITS) == 0L)
throw new IllegalMonitorStateException();
// 如果是最后一个读锁
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - 1L))
return;
}
// 复杂的读锁释放
releaseShared(s, m, stamp);
}
/**
* 释放共享锁(读锁)
*/
private void releaseShared(long s, long m, long stamp) {
// 省略具体实现细节
}
/**
* 通用的锁释放方法
* 根据序列号自动判断是写锁还是读锁
*
* @param stamp 锁的序列号
* @throws IllegalMonitorStateException 如果序列号无效
*/
public void unlock(long stamp) {
long a = stamp & ABITS, m, s; WNode h;
// 验证序列号
if (((s = state) & SBITS) != (stamp & SBITS) ||
(a = stamp & ABITS) == 0L)
throw new IllegalMonitorStateException();
// 根据锁类型调用相应的释放方法
if (a == WBIT) // 写锁
unlockWrite(stamp);
else if (a > 0L) // 读锁
unlockRead(stamp);
}
6. 查询和状态方法(详细注释)
/**
* 判断是否持有写锁
*
* 查询结果:
* - true:当前线程持有写锁
* - false:当前线程未持有写锁
*
* @return 如果持有写锁返回true,否则返回false
*/
public boolean isWriteLocked() {
return (state & WBIT) != 0L;
}
/**
* 判断是否持有读锁
*
* 查询结果:
* - true:有线程持有读锁
* - false:没有线程持有读锁
*
* @return 如果持有读锁返回true,否则返回false
*/
public boolean isReadLocked() {
return (state & RBITS) != 0L;
}
/**
* 获取读锁持有计数
*
* 查询结果:
* - 返回当前持有的读锁总数
* - 包括所有线程持有的读锁
*
* @return 读锁持有计数
*/
public int getReadLockCount() {
return (int)(state & RBITS);
}
/**
* 获取当前序列号
*
* 返回值:
* - 当前的序列号
* - 可用于乐观读的验证
*
* @return 当前序列号
*/
public long getStamp() {
return state & SBITS;
}
/**
* 获取锁的字符串表示
*
* @return 锁的状态信息
*/
public String toString() {
long s = state;
return super.toString() +
" [stamp = " + Long.toString(s & SBITS) +
" wlock = " + ((s & WBIT) != 0L) +
" rlock = " + (s & RBITS) + "]";
}
7. StampedLock 的特点分析
核心设计理念:
/**
* StampedLock的核心设计思想:
*
* 1. 三种锁模式:
* - 写锁:独占锁,与ReentrantReadWriteLock的写锁类似
* - 悲观读锁:共享锁,与ReentrantReadWriteLock的读锁类似
* - 乐观读锁:无锁操作,高性能读取,需要验证
*
* 2. 序列号机制:
* - 使用序列号(stamp)进行版本控制
* - 支持锁状态的验证和转换
* - 提供乐观读的一致性保证
*
* 3. 高性能设计:
* - 乐观读避免了锁的开销
* - 无竞争时使用CAS操作
* - 减少线程阻塞和唤醒的开销
*
* 4. 灵活的锁转换:
* - 支持写锁转读锁
* - 支持读锁转写锁
* - 支持乐观读转写锁
* - 减少锁的获取和释放次数
*
* 5. 非可重入性:
* - 不支持锁的重入
* - 简化实现,提高性能
* - 需要程序员确保正确使用
*
* 6. 不支持Condition:
* - 没有newCondition方法
* - 简化实现,专注于高性能读写
*
* 适用场景:
* - 读操作远多于写操作的场景
* - 对读性能要求极高的场景
* - 可以容忍短暂不一致的场景
* - 需要灵活锁转换的场景
*/
性能特征分析:
/**
* StampedLock的性能特征:
*
* 时间复杂度:
* - 乐观读:O(1) - 无锁操作
* - 悲观读:O(1) 无竞争,O(n) 有竞争
* - 写锁:O(1) 无竞争,O(n) 有竞争
* - 锁转换:O(1) 直接转换,O(n) 复杂转换
*
* 空间复杂度:
* - O(1) 基本状态开销
* - O(n) 等待队列开销
* - O(1) 读线程数组开销
*
* 并发特性:
* - 完全线程安全
* - 支持高并发读取
* - 读写互斥保证数据一致性
* - 无锁设计(通过CAS)
*
* 性能对比:
* - 乐观读:性能最高,无锁开销
* - 悲观读:性能优于ReentrantReadWriteLock
* - 写锁:性能与ReentrantReadWriteLock相当
* - 锁转换:减少锁获取/释放开销
*
* 内存使用:
* - 每个实例固定开销较小
* - 等待线程会增加内存使用
* - 及时GC,避免内存泄漏
*
* 适用性:
* - 读多写少:性能优势明显
* - 高频读取:乐观读提供极致性能
* - 灵活转换:减少锁操作开销
* - 简单场景:避免ReentrantLock的复杂性
*/
8. 使用示例和最佳实践
/**
* 使用示例:
*
* // 基本使用
* StampedLock lock = new StampedLock();
* private double x, y; // 共享数据
*
* // 乐观读模式
* void moveIfAtOrigin(double newX, double newY) {
* // 尝试乐观读
* long stamp = lock.tryOptimisticRead();
* // 读取数据
* double currentX = x;
* double currentY = y;
* // 验证数据一致性
* if (!lock.validate(stamp)) {
* // 验证失败,使用悲观读锁
* stamp = lock.readLock();
* try {
* currentX = x;
* currentY = y;
* } finally {
* lock.unlockRead(stamp);
* }
* }
* // 检查条件并执行操作
* if (currentX == 0.0 && currentY == 0.0) {
* // 需要修改数据,获取写锁
* stamp = lock.writeLock();
* try {
* // 重新检查条件(双重检查)
* if (x == 0.0 && y == 0.0) {
* x = newX;
* y = newY;
* }
* } finally {
* lock.unlockWrite(stamp);
* }
* }
* }
*
* // 悲观读模式
* double distanceFromOrigin() {
* long stamp = lock.readLock();
* try {
* return Math.sqrt(x * x + y * y);
* } finally {
* lock.unlockRead(stamp);
* }
* }
*
* // 写模式
* void move(double deltaX, double deltaY) {
* long stamp = lock.writeLock();
* try {
* x += deltaX;
* y += deltaY;
* } finally {
* lock.unlockWrite(stamp);
* }
* }
*
* // 锁转换示例
* void complexOperation() {
* // 获取写锁
* long writeStamp = lock.writeLock();
* try {
* // 执行一些写操作
* performWriteOperations();
*
* // 转换为读锁(如果可能)
* long readStamp = lock.tryConvertToReadLock(writeStamp);
* if (readStamp != 0) {
* // 转换成功,释放读锁
* writeStamp = 0L; // 避免重复释放
* // 执行读操作
* performReadOperations();
* lock.unlockRead(readStamp);
* } else {
* // 转换失败,继续使用写锁
* performReadOperations();
* }
* } finally {
* if (writeStamp != 0L) {
* lock.unlockWrite(writeStamp);
* }
* }
* }
*
* 最佳实践:
*
* 1. 正确使用乐观读:
* StampedLock lock = new StampedLock();
* private volatile Data data;
*
* Data getData() {
* // 尝试乐观读
* long stamp = lock.tryOptimisticRead();
* Data currentData = data;
* // 验证数据一致性
* if (!lock.validate(stamp)) {
* // 验证失败,使用悲观读锁
* stamp = lock.readLock();
* try {
* currentData = data;
* } finally {
* lock.unlockRead(stamp);
* }
* }
* return currentData;
* }
*
* 2. 正确的锁获取和释放:
* StampedLock lock = new StampedLock();
*
* // 读操作
* long stamp = lock.readLock();
* try {
* // 读取操作
* readData();
* } finally {
* lock.unlockRead(stamp); // 必须在finally块中释放
* }
*
* // 写操作
* stamp = lock.writeLock();
* try {
* // 写入操作
* writeData();
* } finally {
* lock.unlockWrite(stamp); // 必须在finally块中释放
* }
*
* 3. 使用锁转换优化性能:
* void optimizedOperation() {
* long stamp = lock.writeLock();
* try {
* // 写操作
* updateData();
*
* // 尝试转换为读锁
* long newStamp = lock.tryConvertToReadLock(stamp);
* if (newStamp != 0) {
* stamp = newStamp; // 转换成功
* // 读操作
* readUpdatedData();
* } else {
* // 转换失败,继续使用写锁
* readUpdatedData();
* }
* } finally {
* lock.unlock(stamp); // 通用释放方法
* }
* }
*
* 4. 避免锁的重入:
* // StampedLock不支持重入,以下代码会导致死锁
* long stamp = lock.writeLock();
* try {
* // 错误:不能再次获取写锁
* // long stamp2 = lock.writeLock();
*
* // 正确:检查是否已持有锁
* if (lock.isWriteLocked()) {
* // 直接操作数据,不重新获取锁
* performNestedOperation();
* }
* } finally {
* lock.unlockWrite(stamp);
* }
*
* 5. 处理异常:
* long stamp = lock.writeLock();
* try {
* // 可能抛出异常的代码
* riskyOperation();
* } catch (Exception e) {
* // 处理异常
* System.err.println("Operation failed: " + e.getMessage());
* throw e; // 重新抛出或处理
* } finally {
* lock.unlockWrite(stamp); // 确保锁被释放
* }
*
* 6. 监控锁的使用情况:
* StampedLock lock = new StampedLock();
*
* // 检查锁的状态
* if (lock.isWriteLocked()) {
* System.out.println("Write lock is currently held");
* }
*
* if (lock.isReadLocked()) {
* System.out.println("Read lock is currently held");
* System.out.println("Read lock count: " + lock.getReadLockCount());
* }
*
* // 获取当前序列号
* long currentStamp = lock.getStamp();
* System.out.println("Current stamp: " + currentStamp);
*
* 7. 合理使用乐观读:
* // 适用于读操作频繁且写操作较少的场景
* Data getCachedData() {
* long stamp = lock.tryOptimisticRead();
* Data result = cachedData;
* if (!lock.validate(stamp)) {
* // 缓存失效,使用悲观读锁
* stamp = lock.readLock();
* try {
* result = cachedData;
* } finally {
* lock.unlockRead(stamp);
* }
* }
* return result;
* }
*
* // 不适用于以下场景:
* void updateWithValidation() {
* long stamp = lock.tryOptimisticRead();
* Data oldData = data;
* if (!lock.validate(stamp)) {
* // 这种模式不适合,应该直接使用写锁
* stamp = lock.writeLock();
* try {
* data = newData;
* } finally {
* lock.unlockWrite(stamp);
* }
* }
* }
*
* 8. 避免长时间持有锁:
* // 错误的做法:长时间持有锁
* long stamp = lock.writeLock();
* try {
* // 大量计算或I/O操作
* expensiveOperation(); // 不应该在锁内执行
* // 更新共享数据
* } finally {
* lock.unlockWrite(stamp);
* }
*
* // 正确的做法:减少锁持有时间
* Object result = expensiveOperation(); // 在锁外执行
* long stamp = lock.writeLock();
* try {
* // 只在必要时持有锁
* updateSharedData(result);
* } finally {
* lock.unlockWrite(stamp);
* }
*
* 9. 使用通用释放方法:
* long stamp = lock.writeLock();
* try {
* // 操作
* performOperation();
* } finally {
* // 使用通用的unlock方法,自动判断锁类型
* lock.unlock(stamp);
* }
*
* 10. 性能调优:
* // 在高并发读场景下监控性能
* StampedLock lock = new StampedLock();
*
* // 定期检查锁的使用情况
* if (lock.getReadLockCount() > 1000) {
* System.out.println("High read contention detected");
* // 可能需要优化数据结构或访问模式
* }
*
* // 监控乐观读的成功率
* // 可以通过自定义监控来跟踪乐观读的验证失败率
*/
9. 与其他同步机制的比较
/**
* StampedLock vs ReentrantReadWriteLock vs ReentrantLock vs synchronized:
*
* StampedLock:
* - 支持乐观读,性能最高
* - 不支持重入
* - 不支持Condition
* - 支持锁转换
* - 适用于读多写少的场景
*
* ReentrantReadWriteLock:
* - 支持读写锁重入
* - 写锁支持Condition
* - 不支持乐观读
* - 不支持锁转换
* - 实现相对复杂
*
* ReentrantLock:
* - 支持重入
* - 支持Condition
* - 不区分读写操作
* - 不支持乐观读
* - 实现简单可靠
*
* synchronized:
* - JVM内置锁
* - 自动释放锁
* - JVM优化(偏向锁、轻量级锁等)
* - 不支持读写分离
* - 不支持乐观读
*
* 性能对比:
* - 乐观读:StampedLock > 所有其他锁
* - 悲观读:StampedLock ≥ ReentrantReadWriteLock
* - 写操作:StampedLock ≈ ReentrantReadWriteLock
* - 无竞争:synchronized ≥ ReentrantLock ≥ StampedLock
* - 有竞争:各种锁性能相近
*
* 功能对比:
* - 乐观读:StampedLock独有
* - 锁转换:StampedLock独有
* - 重入性:ReentrantLock、ReentrantReadWriteLock、synchronized支持
* - Condition:ReentrantLock、ReentrantReadWriteLock写锁支持
* - 简单性:synchronized最简单
*
* 选择建议:
* - 高频读取且写少:StampedLock
* - 需要重入:ReentrantLock、ReentrantReadWriteLock
* - 需要Condition:ReentrantLock、ReentrantReadWriteLock写锁
* - 简单同步需求:synchronized
* - 复杂同步逻辑:ReentrantLock
*
* 使用场景:
* - 缓存系统:StampedLock(乐观读)
* - 数据库访问:ReentrantReadWriteLock
* - 简单互斥:synchronized
* - 复杂协调:ReentrantLock
* - 高性能读:StampedLock
*/
10. 总结
StampedLock 的核心特性:
-
三种锁模式:
- 乐观读锁:无锁操作,高性能读取
- 悲观读锁:共享锁,多个读线程可并发
- 写锁:独占锁,写线程独占访问
-
序列号机制:
- 使用stamp进行版本控制
- 支持数据一致性验证
- 提供灵活的锁转换
-
高性能设计:
- 乐观读避免锁开销
- 无竞争时使用CAS操作
- 减少线程阻塞和唤醒
-
灵活的锁转换:
- 支持写锁转读锁
- 支持读锁转写锁
- 支持乐观读转写锁
-
非可重入性:
- 简化实现,提高性能
- 需要程序员确保正确使用
-
不支持Condition:
- 专注于高性能读写
- 简化实现复杂度
适用场景:
- 读操作远多于写操作的场景
- 对读性能要求极高的场景
- 可以容忍短暂不一致的场景
- 需要灵活锁转换的场景
- 高并发的缓存系统
- 高频读取的数据结构
注意事项:
- 不支持锁的重入
- 必须正确配对锁的获取和释放
- 乐观读需要配合validate方法使用
- 不支持Condition等待/通知
- 需要程序员确保线程安全
- 避免长时间持有锁
性能优化建议:
- 根据读写比例选择合适的锁模式
- 合理使用乐观读提高读性能
- 利用锁转换减少锁操作开销
- 监控锁的使用情况和性能指标
- 优化临界区代码减少持有时间
- 在读多写少的场景下充分发挥优势
- 定期分析和调优锁的使用模式
- 避免不必要的锁竞争和阻塞

462

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



