StampedLock自编
- 重入锁ReentrantLock以及synchronized 是一种典型的悲观策略。StampedLock就是提供了一种乐观锁的工具,因此,它是对乐观锁的一个重要的补充。
- 它通过 CAS(Compare-And-Swap)操作和自旋重试来减少线程阻塞和上下文切换的开销,高性能、低竞争的锁机制,尤其适用于读多写少的场景。
- 和重入锁的比较
1. 【编程复杂】编程复杂度来说,StampedLock其实是要比重入锁复杂的多
2. 【性能好】StampedLock性能要比普通的重入锁快几倍
3. StampedLock它是不可重入的
4. 不支持wait/notify的Object类线程api【wait()让当前线程释放锁并进入等待状态。notifyAll():唤醒所有在该锁上等待的线程。notify():随机唤醒一个在该锁上等待的线程。】 - 内部数据结构:
1. 有一个链表队列,里面存放着等待上锁的线程。
2. StampedLock中另外一个特别重要的字段就是long state, 这是一个64位的整数,前56位记录写锁释放的次数,如果有写锁占用就让第8位设置为1 ,最后7位记录读锁的线程数量(因此只能记录可怜的126个,超过了记录在readerOverflow)。【前56位为什么要记录写锁释放的次数:解决了CAS操作可能会遇到ABA问题:如果不记录次数,那么当写锁释放掉,申请到,再释放掉时,我们将无法判断数据是否被写过。而这里记录了释放的次数,因此出现"释放->申请->释放"的时候,CAS操作就可以检查到数据的变化,从而判断写操作已经有发生,作为一个乐观锁来说,就可以准确判断冲突已经产生,剩下的就是交给应用来解决冲突即可。因此,这里记录释放锁的次数,是为了精确地监控线程冲突。】
-
public boolean validate(long stamp)
它的接受参数是上次锁操作返回的邮戳,如果在调用validate()之前,这个锁没有写锁申请过,那就返回true,这也表示锁保护的共享数据并没有被修改,因此之前的读取操作是肯定能保证数据完整性和一致性的。
反之,如果锁在validate()之前有写锁申请成功过,那就表示,之前的数据读取和写操作冲突了,程序需要进行重试,或者升级为悲观锁。