JUC多并发编程 StampedLock

简介:

  • StampedLock 是 JDK1.8 中新增的一个读写锁,也是对 JDK1.5 中的读写锁 ReentrantReadWriteLock 的优化。
  • stamp 代表了锁的状态。当 stamp 返回零时,表示线程获取锁失败。并且,当释放锁或者转换锁的时候,都需要传入最初获取的 stamp 值。
  • 采取乐观获取锁后,其他线程尝试获取写锁时不会被阻塞,在获取乐观读锁后,还需要对结果进行校验
  • 对短的只读代码段,使用乐观模式通常可以减少争用并提高吞吐量

锁饥饿:

  • ReentrantReadWriteLock 实现了读写分离,但是一旦读操作比较多的时候,想要获取写锁就变得比较困难了。
  • 使用”公平“策略可以一定程度上缓解这个问题,但是是以牺牲系统吞吐量为代价的。 new ReentrantReadWriteLock(true)

特点:

  • 所有获取锁的方法,都返回一个邮戳(Stamp),Stamp 为零表示获取失败,其余都表示成功
  • 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp 必须是和成功获取锁时得到的Stamp 一致
  • StampedLock 是不可重入的,危险(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)

访问模式:

  • Reading(读模式悲观): 功能和ReentrantReadWriteLock的读锁类似
  • Writing(写模式): 功能和ReentrantReadWriteLock的写锁类似
  • Optimistic reading(乐观读模式): 无锁机制, 类似于数据库中的乐观锁,支持读写并发,很乐观认为读取时没人修改,假如被修改再实现升级为悲观读模式

示例代码:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

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

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

    public void read(){
        long l = stampedLock.readLock();
        System.out.println(Thread.currentThread().getName()+"\t"+"come in readLock code");
        for (int i = 0; i < 4; i++) {
            try{ TimeUnit.MILLISECONDS.sleep(1000); } 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("写线程没有修改成功,读锁时候写锁无法接入,传统的读写互斥");
        }finally {
            stampedLock.unlockRead(l);
        }
    }

    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 = 0; i < 4; i++) {
            try{ TimeUnit.MILLISECONDS.sleep(1000); } 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);
            }finally {
                stampedLock.unlockRead(stamp);
            }
        }
        System.out.println(Thread.currentThread().getName()+"\t"+ "finally value:" + result);
    }
    public static void main(String[] args) {
        StampedLockDemo stampedLockDemo = new StampedLockDemo();

        new Thread(()->{
            // stampedLockDemo.read();
            // 乐观读
            stampedLockDemo.tryOptimisticRead();
        },"readLock").start();
        // 写介入
        try{ TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
        // 写无介入
        // try{ TimeUnit.MILLISECONDS.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t"+"come in");
            stampedLockDemo.write();
        },"writeLock").start();
        try{ TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println(Thread.currentThread().getName()+"\t"+"number:" + number);

    }
}

缺点:

  • StampedLock 不支持重入,没有 Re 开头
  • StampedLock 的悲观读锁和写锁不支持条件变量(Condition)
  • 使用 StampedLock 一定不要调用中断操作,即不要调用 interrupt() 方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值