JDK8引入
主要是有乐观读, 就是完全无锁, 读到以后再去检查下版本, 不行再说
好处
比ReentrantReadWriteLock
的改进就是加了乐观读, 这样了避免了 ReentrantReadWriteLock
非公平锁时 读多写少, 写入队以后一直轮不到,饿死的情况, 还提高了吞吐量
另外, 可以升级 也可以降级
坏处
和ReentrantReadWriteLock
比, 功能少了, 只是一个子集
- 不支持条件变量
- 不能中断线程
- 不能重入
而且 ,本来是为内部使用的, 用起来一定要按模板,比较复杂
乐观读
不能保证读到是最新的, 可以拿到以后再验证, 完全无锁, 性能好
使用 都是这个模板
public String get(Integer key) {
// 1. 尝试通过乐观读模式读取数据,非阻塞
long stamp = lock.tryOptimisticRead();
// 2. 读取数据到当前线程栈
String currentValue = idMap.get(key);
// 3. 校验是否被其他线程修改过,true 表示未修改,否则需要加悲观读锁
if (!lock.validate(stamp)) {
// 4. 上悲观读锁,并重新读取数据到当前线程局部变量
stamp = lock.readLock();
try {
currentValue = idMap.get(key);
} finally {
lock.unlockRead(stamp);
}
}
// 5. 若校验通过,则直接返回数据
return currentValue;
}
原理
比ReentrantReadWriteLock
性能要高,内部没用AQS 但是 和AQS原理是一样的 都是用的 CLH队列

一样是双向链表, 有存了 头尾 字段, 有个状态
state
也是状态2 用, 又表示写锁又记读锁
注意有个NCPU
字段, 记录的是核数, 如果核数>1 , 获取锁和入队 都有自旋
CLH队列的节点比AQS更加简单, 只有3种状态

另外多了一个 cowait
字段, 记录读线程, 头插法

这样 如果前一个节点就是读, 下一个读就连入
cowait
了, 不像AQS往后连,而且因为是头插法, 晚来的读 反而先叫起来, 不过反正不读之间不阻塞, 差不多
得锁 解锁
获取写锁:

cas试试, 失败就入队,
入队有大量自旋操作, 就是入前文所述的 CLH
读锁也差不多
释放锁:
和ReentrantReadWriteLock
差不多 都改state, 叫醒队列下一个, 就是前面多了一步, 核对传入的stamp
, 如果对不上会抛出异常
网友评论