并发编程系列之StampedLock使用

x33g5p2x  于2021-12-30 转载在 其他  
字(2.6k)|赞(0)|评价(0)|浏览(403)

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;
    }

}

附录参考资料

相关文章