StampedLock类的简介

StampedLock类,在JDK1.8时引入,是对读写锁ReentrantReadWriteLock的增强,该类提供了一些功能,优化了读锁、写锁的访问,同时使读写锁之间可以互相转换,更细粒度控制并发。它的特点是在使用读锁、写锁时都必须配合【戳】使用,如:
image.png
这个戳通常会配合该类的tryOptimisticRead方法使用,tryOptimisticRead方式采用的是乐观读的思想。在ReentrantReadWriteLock中,当读锁被使用时,如果有线程尝试获取写锁,该写线程会阻塞。但是,在Optimistic reading中,即使读线程获取到了读锁,写线程尝试获取写锁也不会阻塞,这相当于对读模式的优化,但是可能会导致数据不一致的问题。所以,当使用Optimistic reading获取到读锁时,必须对获取结果使用戳进行校验。

StampedLock的特点

  • StampedLock 是从 JDK1.8 开始提供,它的性能比 ReadWriteLock 好
  • StampedLock 支持:乐观读锁、悲观读锁、写锁
  • StampedLock 不支持重入
  • StampedLock 支持锁的降级和升级 (即乐观读锁升级为写锁)
  • StampedLock 无论写锁还是读锁,都不支持Conditon等待

    StampedLock使用示例

    1. @Slf4j(topic = "c.DataContainerStamped")
    2. public class DataContainerStamped {
    3. private int data;
    4. private final StampedLock lock = new StampedLock();
    5. public DataContainerStamped(int data) {
    6. this.data = data;
    7. }
    8. public void read(int readTime) throws InterruptedException {
    9. long stamp = lock.tryOptimisticRead();
    10. log.debug("optimistic read locking...{}", stamp);
    11. Thread.sleep(readTime * 1000L);
    12. //validate方法用于验戳,即验证数据是否被修改
    13. if (lock.validate(stamp)) {
    14. log.debug("read finish...{}, data:{}", stamp, data);
    15. return;
    16. }
    17. // 锁升级 - 读锁(悲观读锁)
    18. log.debug("updating to read lock... {}", stamp);
    19. try {
    20. stamp = lock.readLock();
    21. log.debug("read lock {}", stamp);
    22. Thread.sleep(readTime * 1000L);
    23. log.debug("read finish...{}, data:{}", stamp, data);
    24. } finally {
    25. log.debug("read unlock {}", stamp);
    26. lock.unlockRead(stamp);
    27. }
    28. }
    29. public void write(int newData) throws InterruptedException {
    30. long stamp = lock.writeLock();
    31. log.debug("write lock {}", stamp);
    32. try {
    33. Thread.sleep(2 * 1000L);
    34. this.data = newData;
    35. } finally {
    36. log.debug("write unlock {}", stamp);
    37. lock.unlockWrite(stamp);
    38. }
    39. }
    40. }

    测试代码:

    1. public static void main(String[] args) throws InterruptedException {
    2. DataContainerStamped dataContainer = new DataContainerStamped(1);
    3. new Thread(() -> {
    4. try {
    5. dataContainer.read(1);
    6. } catch (InterruptedException e) {
    7. e.printStackTrace();
    8. }
    9. }, "t1").start();
    10. new Thread(() -> {
    11. try {
    12. dataContainer.write(2);
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }
    16. }, "t2").start();
    17. }

    测试结果:
    image.png
    可以看到在“01:28:24”,t1获取乐观读锁,随后t2获取写锁,t2并没有被阻塞,因此验证了可以从乐观读锁升级为写锁的特点。StampedLock 相比于读写锁有很多优点,但是有两个比较大的缺点:

    • StampedLock 不支持条件变量
    • StampedLock 不支持可重入