ReadWriteLock和StampedLock

简介

readWriteLock 可以实现多个读锁同时进行,但是读与写和写与写只能互斥,只能由一个写锁的线程正在进行

StampedLock是在1.8提供的一种读写锁,相比较ReentrantReadWriteLock性能更好。因为ReentrantReadWriteLock在读写之间是互斥的,使用的是悲观策略,在读线程特别多的情况下,会造成线程处于饥饿的状态,虽然可以在初始化的时候设置为true。但是吞吐量又下去了,而StampedLock提供了一种乐观策略,更好的实现读写分离,并且吞吐量不会下降

StampedLock

writeLock是一个独占锁写锁,当一个线程获得该锁后,其他请求或者写锁线程阻塞,获取成功后,会返回一个stamp(凭据)变量来表示该锁的版本,在释放锁时调用unlockWrite方法传递stamp参数。提供了非阻塞获取锁trWriteLock

加锁解锁写锁

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

加锁解锁读锁

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

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

//乐观读
long stamp = lock.tryOptimisticRead();
// 验戳失败 
if(!lock.validate(stamp)){
// 锁升级 为悲观读readLock
}

由于tryOptimisticRead没有修改锁的状态所以不需要释放锁

使用

public class StampedLockDemo
{
    static int number = 37;
    static StampedLock stampedLock = new StampedLock();

    //写
    public void write()
    {
        long stamp = stampedLock.writeLock();
        System.out.println(Thread.currentThread().getName()+"\t"+"=====写线程准备修改");
        try
        {
            number = number + 13;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            stampedLock.unlockWrite(stamp);
        }
        System.out.println(Thread.currentThread().getName()+"\t"+"=====写线程结束修改");
    }

    //悲观读
    public void read()
    {
        long stamp = stampedLock.readLock();
        System.out.println(Thread.currentThread().getName()+"\t come in readlock block,4 seconds continue...");
        //暂停几秒钟线程
        for (int i = 0; i <4 ; i++) {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t 正在读取中......");
        }
        try
        {
            int result = number;
            System.out.println(Thread.currentThread().getName()+"\t"+" 获得成员变量值result:" + result);
            System.out.println("写线程没有修改值,因为 stampedLock.readLock()读的时候,不可以写,读写互斥");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            stampedLock.unlockRead(stamp);
        }
    }

    //乐观读
    public void tryOptimisticRead()
    {
        long stamp = stampedLock.tryOptimisticRead();
        int result = number;
        //间隔4秒钟,我们很乐观的认为没有其他线程修改过number值,实际靠判断。
        System.out.println("4秒前stampedLock.validate值(true无修改,false有修改)"+"\t"+stampedLock.validate(stamp));
        for (int i = 1; i <=4 ; i++) {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t 正在读取中......"+i+
                    "秒后stampedLock.validate值(true无修改,false有修改)"+"\t"
                    +stampedLock.validate(stamp));
        }
        if(!stampedLock.validate(stamp)) {
            System.out.println("有人动过--------存在写操作!");
            stamp = stampedLock.readLock();//锁升级为悲观读
            try {
                System.out.println("从乐观读 升级为 悲观读");
                result = number;
                System.out.println("重新悲观读锁通过获取到的成员变量值result:" + result);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                stampedLock.unlockRead(stamp);
            }
        }
        System.out.println(Thread.currentThread().getName()+"\t finally value: "+result);
    }

    public static void main(String[] args)
    {
        StampedLockDemo resource = new StampedLockDemo();

        new Thread(() -> {
            resource.read();
            //resource.tryOptimisticRead();
        },"readThread").start();

        // 2秒钟时乐观读失败,6秒钟乐观读取成功resource.tryOptimisticRead();,修改切换演示
        //try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            resource.write();
        },"writeThread").start();
    }
}

StampedLock 不支持重入,没有Re开头

StampedLock 的悲观读锁和写锁都不支持条件变量(Condition),这个也需要注意。

使用 StampedLock一定不要调用中断操作,即不要调用interrupt() 方法

如果需要支持中断功能,一定使用可中断的悲观读锁 readLockInterruptibly()和写锁writeLockInterruptibly()

Last modification:January 29, 2023
如果觉得我的文章对你有用,请随意赞赏