并发编程系列之StampedLock使用

Posted smileNicky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程系列之StampedLock使用相关的知识,希望对你有一定的参考价值。

1、什么是StampedLock?

  • StampedLock,也即邮戳锁,是jdk8中推出的对读写锁的缺点进行改进的邮戳锁,它推出了乐观读写来改进大量并发读,少量写的情况的性能。
  • StampedLock是不可重入的,使用时需特别注意。

2、StampedLock三种模式

StampedLock有三种模式,读写 可以 相互转换:

  • Writing:写模式
  • Reading:悲观读模式
  • Optimistic Reading:乐观读模式

一个StampedLock状态是由票据和模式两个部分组成的,锁获取方法返回一个数字作为票据stamp,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。在读锁上分为悲观锁和乐观锁。

3、StampedLock相关API

  • 获得独占写锁:
    • long writeLock():阻塞式获取锁
    • long tryWriteLock():尝试获取锁,如返回0表示没有获得锁
    • long tryWriteLock(long time, TimeUnit unit):尝试获取锁,如返回0表示没有获得锁
    • long writeLockInterruptibly() :可中断式获取读锁
  • 释放写锁:
    • void unlockWrite(long stamp)
    • boolean tryUnlockWrite:尝试释放写锁,如果线程持有写锁,释放,返回true,否则返回false
  • 获取读锁:
    • long readLock():阻塞式非独占获取锁 。
    • long tryReadLock():尝试获得写锁,如返回0表示没有获得锁
    • long tryReadLock(long time , TimeUnit unit):尝试获得写锁,如返回0表示没有获得锁
    • long readLockInterruptibly():可中断式获取读锁
  • 释放读锁:
    • void unlockRead (long stamp)
    • boolean tryUnlockRead():尝试释放对读锁的一个持有;如果持有,则释放,返回true,否则返回false
  • 获取乐观读锁
    • long tryOptimisticRead():当被其它线程占用时,将返回0。
  • 转换模式:
    • long tryConvertToOptimisticRead(long stamp)
    • long tryConvertToReadLock(long stamp)
    • long tryConvertToWriteLock(long stamp)
  • 判断锁的当前状态(模式)
    • boolean isWriteLocked()
    • boolean isReadLocked()
  • 为了优化老代码中的读写锁,StampedLock提供了三个方法将自身转为读写锁:
    • Lock asReadLock()
    • Lock asWriteLock()
    • ReadWriteLock asReadWriteLock()
  • 其它API
    • boolean validate(long stamp):参数为乐观锁票据
    • void unlock(long stamp):释放锁,如果锁的状态匹配给入的stamp,释放锁的对应模式

StampedLock例子

例子:使用StampedLock 来做缓存的并发控制,有缓存put和get方法:


import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;

public class StampedLockCache 

    private final Map<Integer , String> cache = new ConcurrentHashMap<>();
    private final StampedLock stampedLock = new StampedLock();

    public void put(Integer key , String value) 
        // 上写锁
        long stamp = stampedLock.writeLock();
        try 
            cache.put(key , value);
         finally 
            // 释放写锁
            stampedLock.unlockWrite(stamp);
        
    

    public String get(Integer key) 
        // 先获取乐观锁
        long stamp = stampedLock.tryOptimisticRead();
        // 先尝试通过乐观锁方式读取数据
        String value = cache.get(key);
        // 校验是否被其它线程修改过,true:表示未修改 false:修改过,表示需要加悲观锁
        if (!stampedLock.validate(stamp)) 
            // 上悲观锁
            stamp = stampedLock.readLock();
            try 
                value = cache.get(key);
             finally 
                stampedLock.unlock(stamp);
            
        
        return value;
    

    public String putIfNotExist(Integer key , String value) 
        long stamp = stampedLock.readLock();
        String currentValue = cache.get(key);
        try 
            while (StringUtils.isEmpty(currentValue)) 
                // 尝试锁升级,读锁升级为写锁
                long wstamp = stampedLock.tryConvertToWriteLock(stamp);
                if (wstamp != 0L)  // 不为0表示锁升级成功
                    stamp = wstamp;
                    currentValue = value;
                    // 数据写到缓存里
                    cache.put(key , value);
                    break;
                
                else  // 锁升级失败
                    // 释放读锁
                    stampedLock.unlockRead(stamp);
                    stamp = stampedLock.writeLock();
                
            
        finally 
            // 释放所有的锁
            stampedLock.unlock(stamp);
        
        return currentValue;
    



附录参考资料

以上是关于并发编程系列之StampedLock使用的主要内容,如果未能解决你的问题,请参考以下文章

Java并发编程之StampedLock锁源码探究

JUC并发编程 共享模式之工具 JUC 读写锁 StampedLock -- 介绍 & 使用

一文彻底理解并发编程中非常重要的票据锁——StampedLock

一文彻底理解并发编程中非常重要的票据锁——StampedLock

Java Review - 并发编程_StampedLock锁探究

Java Review - 并发编程_StampedLock锁探究