不可重入锁-StampedLock

前言

什么叫不可重入:

同一个线程获取到锁时,不能执行相同锁的方法块。

JDK1.8: StampedLock

stamp : 英译“票据”,票据锁

与ReadWritelock的区别:

读写锁:只有读和读操作是共享,其它读和写操作都是互斥

票据锁:支持乐观读

StampedLock有三种读/写模式:写、读、乐观读:

  • 写。独占锁

只有当前没有线程持有读锁或者写锁时才能获取到该锁。方法writeLock()返回一个可用于unlockWrite(long)释放锁的方法的戳记。tryWriteLock()提供不计时和定时的版本。

  • 读。共享锁

如果当前没有线程持有写锁即可获取该锁,可以由多个线程获取到该锁。方法readLock()返回可用于unlockRead(long)释放锁的方法的戳记。tryReadLock()也提供不计时和定时的版本。

  • 乐观读。方法tryOptimisticRead()

仅当锁定当前未处于写入模式时,方法才会返回非零戳记。返回戳记后,需要调用validate(long stamp)方法验证戳记是否可用。也就是看当调用tryOptimisticRead返回戳记后到到当前时间是否有其他线程持有了写锁,如果有,返回false,否则返回true,这时就可以使用该锁了。

转换

此类还支持有条件地提供三种模式转换的方法。例如,方法tryConvertToWriteLock(long)试图“升级”模式,如果(1)已经处于书写模式(2)处于阅读模式并且没有其他读取器或者(3)处于乐观模式且锁定可用,则返回有效的写入标记。这些方法的形式旨在帮助减少在基于重试的设计中发生的一些代码膨胀。

StampedLock不是可重入的。

下面是一个StampedLock注释中的例子。

class Point {
    private double x, y;
    private final StampedLock sl = new StampedLock();
    /**
     * 改变当前坐标。
     * 先获取写锁,然后对point坐标进行修改,最后释放锁。
     * 该锁是排它锁,这保证了其他线程调用move函数时候会被阻塞,直到当前线程显示释放了该锁。
     */
    void move(double deltaX, double deltaY) { // an exclusively locked method
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }
    /**
     * 计算当前坐标到原点的距离
     * 
     * @return
     */
    double distanceFromOrigin() { // A read-only method
        //1.尝试获取乐观读锁,返回stamp
        long stamp = sl.tryOptimisticRead();
        //2.拷贝参数到本地方法栈中
        double currentX = x, currentY = y;
        //3.验证stamp是否有效
        if (!sl.validate(stamp)) {
            //4.如果stamp无效,说明得到stamp后,又有其他线程获得了写锁
            //5.获取读锁
            stamp = sl.readLock();
            try {
                //6.其他线程修改了x,y的值,为了数据的一致性,需要再次再次拷贝参数到本地方法栈中
                currentX = x;
                currentY = y;
            } finally {
                //7.释放读锁
                sl.unlockRead(stamp);
            }
        }
        //8.使用参数的拷贝来计算当前坐标到原点的距离。无论步骤3中stamp有没有验证成功,参数的拷贝都是当前坐标的值
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
    /**
     * 如果当前坐标为原点则移动到指定的位置
     */
    void moveIfAtOrigin(double newX, double newY) { // upgrade
        // 获取读锁,保证其他线程不能获取到写锁
        long stamp = sl.readLock();
        try {
            //如果当前坐标为原点
            while (x == 0.0 && y == 0.0) {
                //尝试升级成写锁
                long ws = sl.tryConvertToWriteLock(stamp);
                //如果升级成功,更新坐标值
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    break;
                } else {//如果升级成功
                    sl.unlockRead(stamp);//先释放读锁
                    stamp = sl.writeLock();//再获取写锁
                    //循环while中的操作,直到成功更新坐标值
                }
            }
        } finally {
            //最后释放写锁
            sl.unlock(stamp);
        }
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值