「乐观读」到底是不是无锁?这篇用小实验把 stamp、validate、读到不一致数据的可能性和吞吐一个个量出来(ReentrantLock 的排队见前面 AQS 一篇)。
tryOptimisticRead() 得到 stamp = 256,没有写者时 validate = true 写锁被持有(stamp = 384)期间,旧 stamp 的 validate = false 写锁释放之后,旧 stamp 的 validate = false(写过一次,乐观读的版本永远失效);新的 tryOptimisticRead() = 512 写锁被持有时 tryOptimisticRead() 返回 0(0 表示失败) 只有读锁被持有时 tryOptimisticRead() 返回 非 0(成功),不加任何锁、也不阻塞写者:tryWriteLock 在读锁被持有时 失败 不可重入:持有写锁时再 tryWriteLock() = 0(0 = 失败,不会像 ReentrantLock 那样重入);同一线程再 writeLock() 会永远阻塞(这里没有执行)
写线程持续修改 (x, y)(不变式 x + y == 100);读线程做了 34581706 次乐观读:
验证之前读到 x + y != 100 的次数 = 2417(读到了写到一半的数据)
validate() 返回 false 的次数 = 34484958;validate 通过却读到不一致的次数 = 0synchronized 8 个读线程 + 每 10 ms 一次写:读吞吐 40.3 百万次/秒 ReentrantReadWriteLock 8 个读线程 + 每 10 ms 一次写:读吞吐 1.9 百万次/秒 StampedLock 悲观读锁 8 个读线程 + 每 10 ms 一次写:读吞吐 2.4 百万次/秒 StampedLock 乐观读 8 个读线程 + 每 10 ms 一次写:读吞吐 2756.0 百万次/秒