多线程之 StampedLock

77 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

1 简介

是为了进一步优化读性能,它的特点是在使用读锁、写锁时都必须配合【戳】使用

加解读锁

long stamp = lock.readLock();
lock.unlockRead(stamp);

加解写锁

long stamp = lock.writeLock();
lock.unlockWrite(stamp);

乐观读,StampedLock 支持 tryOptimisticRead() 方法(乐观读),读取完毕后需要做一次 戳校验 如果校验通 过,表示这期间确实没有写操作,数据可以安全使用,如果校验没通过,需要重新获取读锁,保证数据安全.

long stamp = lock.tryOptimisticRead();
// 验戳
if(!lock.validate(stamp)){
 // 锁升级
}

2 测试

class DataContainerStamped {
     private int data;
     private final StampedLock lock = new StampedLock();
     public DataContainerStamped(int data) {
         this.data = data;
     }
    
     public int read(int readTime) {
         long stamp = lock.tryOptimisticRead();
         log.debug("optimistic read locking...{}", stamp);
         sleep(readTime);
         if (lock.validate(stamp)) {
             log.debug("read finish...{}, data:{}", stamp, data);
             return data;
         }
         // 锁升级 - 读锁
         log.debug("updating to read lock... {}", stamp);
         try {
             stamp = lock.readLock();
             log.debug("read lock {}", stamp);
             sleep(readTime);
             log.debug("read finish...{}, data:{}", stamp, data);
             return data;
         } finally {
             log.debug("read unlock {}", stamp);
             lock.unlockRead(stamp);
         }
     }
     public void write(int newData) {
         long stamp = lock.writeLock();
         log.debug("write lock {}", stamp);
         try {
             sleep(2);
             this.data = newData;
         } finally {
             log.debug("write unlock {}", stamp);
             lock.unlockWrite(stamp);
         }
     }
}

读-读测试

public static void main(String[] args) {
     DataContainerStamped dataContainer = new DataContainerStamped(1);
     new Thread(() -> {
         dataContainer.read(1);
     }, "t1").start();
     sleep(0.5);
     new Thread(() -> {
         dataContainer.read(0);
     }, "t2").start();
}

运行结果:

07:58:50.217 c.DataContainerStamped [t1] - optimistic read locking...256 
07:58:50.717 c.DataContainerStamped [t2] - optimistic read locking...256 
07:58:50.717 c.DataContainerStamped [t2] - read finish...256, data:1 
07:58:51.220 c.DataContainerStamped [t1] - read finish...256, data:1 

从日志来看,没有加读锁

读-写测试

public static void main(String[] args) {
     DataContainerStamped dataContainer = new DataContainerStamped(1);
     new Thread(() -> {
         dataContainer.read(1);
     }, "t1").start();
     sleep(0.5);
     new Thread(() -> {
         dataContainer.write(100);
     }, "t2").start();
}

运行结果:

07:57:00.219 c.DataContainerStamped [t1] - optimistic read locking...256 
07:57:00.717 c.DataContainerStamped [t2] - write lock 384 
07:57:01.225 c.DataContainerStamped [t1] - updating to read lock... 256 
07:57:02.719 c.DataContainerStamped [t2] - write unlock 384 
07:57:02.719 c.DataContainerStamped [t1] - read lock 513 
07:57:03.719 c.DataContainerStamped [t1] - read finish...513, data:1000 
07:57:03.719 c.DataContainerStamped [t1] - read unlock 513 

从日志来看,有加写锁, 读锁.

与ReentrantLock等相比:

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