有比 ReadWriteLock更快的锁?( 二 )

可以看到,上述示例最特殊的其实是distanceFromOrigin方法,这个方法中使用了“Optimistic reading”乐观读锁,使得读写可以并发执行,但是“Optimistic reading”的使用必须遵循以下模式:
long stamp = lock.tryOptimisticRead();  // 非阻塞获取版本信息copyVaraibale2ThreadMemory();           // 拷贝变量到线程本地堆栈if(!lock.validate(stamp)){              // 校验    long stamp = lock.readLock();       // 获取读锁    try {        copyVaraibale2ThreadMemory();   // 拷贝变量到线程本地堆栈     } finally {       lock.unlock(stamp);              // 释放悲观锁    }}useThreadMemoryVarables();              // 使用线程本地堆栈里面的数据进行操作三、StampedLock原理3.1 StampedLock的内部常量StampedLock虽然不像其它锁一样定义了内部类来实现AQS框架,但是StampedLock的基本实现思路还是利用CLH队列进行线程的管理,通过同步状态值来表示锁的状态和类型 。
StampedLock内部定义了很多常量,定义这些常量的根本目的还是和ReentrantReadWriteLock一样,对同步状态值按位切分,以通过位运算对State进行操作:

对于StampedLock来说,写锁被占用的标志是第8位为1,读锁使用0-7位,正常情况下读锁数目为1-126,超过126时,使用一个名为readerOverflow的int整型保存超出数 。
// 用于计算state值的位常量private static final int LG_READERS = 7;private static final long RUNIT = 1L; // 一单位读锁       0000 0001private static final long WBIT  = 1L << LG_READERS; // 写锁标志位  1000 0000private static final long RBITS = WBIT - 1L; // 读状态标志 0111 1111private static final long RFULL = RBITS - 1L; // 读锁的最大数量 0111 1110private static final long ABITS = RBITS | WBIT; // 用于获取读写状态 1111 1111private static final long SBITS = ~RBITS; // 1111...1000 0000/** * 初始state值*/private static final long ORIGIN = WBIT << 1;/** * 同步状态state,处于写锁使用第8位(为1表示占用),读锁使用前7位(为1~126,附加的readerOverflow用于当读锁超过126时)*/private transient volatile long state;/** * 因为读锁只使用了前7位,所以当超过对应数值之后需要使用一个int型保存 */private transient int readerOverflow;部分常量的比特位表示如下:
有比 ReadWriteLock更快的锁?

文章插图
 
另外,StampedLock相比ReentrantReadWriteLock,对多核CPU进行了优化,可以看到,当CPU核数超过1时,会有一些自旋操作:
/*** CPU核数,用于控制自旋次数*/private static final int NCPU = Runtime.getRuntime().availableProcessors();/** * 尝试获取锁时,如果超过该值仍未获取到锁,则进入等待队列*/private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;/** * 等待队列的首节点,自旋获取锁失败时会,会继续阻塞*/private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;/** * 再次进入阻塞之前的最大重试次数*/private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;3.2 示例分析
假设现在有三个线程:ThreadA、ThreadB、ThreadC、ThreadD 。操作如下:
// ThreadA调用writeLock, 获取写锁
// ThreadB调用readLock, 获取读锁
// ThreadC调用readLock, 获取读锁
// ThreadD调用writeLock, 获取写锁
// ThreadE调用readLock, 获取读锁
1. StampedLock对象的创建StampedLock的构造器很简单,构造时设置下同步状态值:


推荐阅读