zl程序教程

您现在的位置是:首页 >  Java

当前栏目

并发编程系列之StampedLock使用

2023-04-18 12:36:57 时间

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

}

附录参考资料