StampedLock

该类自 JDK 8 加入,是为了进一步优化读性能,它的特点是在使用读锁、写锁时都必须配合【戳】使用

读锁:

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

写锁:

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

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

long stamp = lock.tryOptimisticRead();
// 验戳
if(!lock.validate(stamp)){
 // 锁升级
}

测试代码:

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.StampedLock;
import static  com.dalianpai.Sleeper.sleep;
/**
 * @author WGR
 * @create 2020/12/31 -- 13:55
 */
@Slf4j(topic = "c.TestStampedLock")
public class TestStampedLock {
    public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1);
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.read(0);
        }, "t2").start();
    }
}

@Slf4j(topic = "c.DataContainerStamped")
class DataContainerStamped {
    private int data;
    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        long stamp = lock.tryOptimisticRead();
        log.debug("optimistic read locking...{}", stamp);
        sleep(readTime);
        if (lock.validate(stamp)) {
            log.debug("read finish...{}, data:{}", stamp, data);
            return data;
        }
        // 锁升级 - 读锁
        log.debug("updating to read lock... {}", stamp);
        try {
            stamp = lock.readLock();
            log.debug("read lock {}", stamp);
            sleep(readTime);
            log.debug("read finish...{}, data:{}", stamp, data);
            return data;
        } finally {
            log.debug("read unlock {}", stamp);
            lock.unlockRead(stamp);
        }
    }

    public void write(int newData) {
        long stamp = lock.writeLock();
        log.debug("write lock {}", stamp);
        try {
            sleep(2);
            this.data = newData;
        } finally {
            log.debug("write unlock {}", stamp);
            lock.unlockWrite(stamp);
        }
    }
}


测试 读-读 可以优化

    public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(10);
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.read(0);
        }, "t2").start();
    }
}

image-20201231140233219

测试描述:当t1线程先抢到乐观读锁,然后睡眠10秒,t2晚t1 0.5秒抢到锁,然后直接结束,并没有堵塞住。

测试读-写 时优化读补加读锁

    public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1);
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.write(100);
        }, "t2").start();
    }

image-20201231141057253

测试描述:当t1线程先抢到乐观读锁,然后休眠1秒钟,t2线程晚t1线程0.5秒抢到读锁,然后休眠2秒,这个时候t1线程醒来,验签失败,这个时候就会锁进升级成读锁,但是锁还在写锁那边,只能等写锁释放,读锁才能拿到。

注意:
StampedLock不支持条件变量
StampedLock不支持可重入

posted @ 2020-12-31 14:19  天宇轩-王  阅读(126)  评论(0编辑  收藏  举报