这里就JUC包中的StampedLock做相关介绍
概述
在读多写少的场景下,非常适合使用ReentrantReadWriteLock读写锁。但其也存在一定的弊端,其有可能导致写线程饥饿。为此JDK 8中提供了StampedLock类,其是一个非公平的读写锁。其与ReentrantReadWriteLock相比,不仅提供了传统意义上的悲观读锁和写锁,最大的区别是其还为读操作提供了乐观锁的方法——即所谓的乐观读锁。当然其也有弊端,无论是悲观读锁还是写锁,均不支持条件变量Condition;然后从类名也可以看到其是不可重入锁。需要注意的是,虽然一个线程可以多次获取悲观读锁,但究其原因是因为悲观读锁是共享锁。实际实践中,可以直接通过writeLock、readLock等阻塞式 或 tryWriteLock、tryReadLock等非阻塞式的方式获取锁,也可通过ReadLockView读锁视图、WriteLockView写锁视图、ReadWriteLockView读写锁视图来进行相应锁的操作
基本实践
读锁、写锁
这里就基本的悲观读锁、写锁的使用进行实践,示例如下所示
1 | (MethodSorters.NAME_ASCENDING) |
测试结果如下所示,符合预期
可以看到StampedLock获取锁、释放锁都需要相应的stamp值。为此也可以通过相应的视图类进行操作,如上述代码的test3所示。其相应测试结果所示。可以看到悲观读锁是一个共享锁,而写锁则是一个互斥锁
乐观读锁
可通过tryOptimisticRead获取一个stamp,即所谓的乐观读锁。然后在完成读操作后,通过validate方法对stamp进行检查。由于读过程通常是非原子性的,故需要判断是否存在其他线程在此期间获取到了写锁,对数据进行了修改。造成当前线程读取的数据状态不一致(部分为修改前的,部分为修改后的)。如果在当前线程进行读的过程中发生了修改更新,则检查结果为false。这时再获取悲观读锁进行重读。事实上,由于乐观读锁并没有锁。故其一方面不会阻塞写线程获取写锁,也不需要在结束后释放该锁。示例代码如下所示
1 | public class StampedLockTest2 { |
测试结果如下所示
参考文献
- Java并发编程之美 翟陆续、薛宾田著