Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker 诊断工具


Java 中的锁 StampedLock

Java juc 大约 6633 字

主要方法

  • readLock():获取悲观读锁,阻塞等待。
  • unlockRead():释放读锁。
  • writeLock():获取悲观写锁,阻塞等待。
  • unlockWrite():释放写锁。
  • tryReadLock():尝试获取悲观读锁,获取不到就直接返回。
  • tryOptimisticRead():尝试获取乐观读标记(不是锁,只是一个标记),获取不到就直接返回。
  • validate():验证stamp标记,返回true表示锁没有被悲观锁获取,false反之。

乐观读

tryOptimisticRead()获得一个stamp标记,如果stamp标记为0,表示被悲观锁锁定了,通过validate()方法判断当前获取的乐观锁是否生效。

示例代码

public class StampedLockDemo {

    public static int x;
    public static int y;

    public static void main(String[] args) {
        StampedLock stampedLock = new StampedLock();


        new Thread(() -> {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(LocalDateTime.now() + " Read Thread id#" + Thread.currentThread().getId() + ", begin to get read lock");
            long stamp = stampedLock.readLock();
            System.out.println(LocalDateTime.now() + " Read Thread id#" + Thread.currentThread().getId() + ", get read lock success, stamp=" + stamp);
            try {
                System.out.println(LocalDateTime.now() + " Read Thread id#" + Thread.currentThread().getId() + ", x=" + x + ", y=" + y);
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }).start();

        new Thread(() -> {
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(LocalDateTime.now() + " Write Thread id#" + Thread.currentThread().getId() + ", begin to get write lock");
            long stamp = stampedLock.writeLock();
            System.out.println(LocalDateTime.now() + " Write Thread id#" + Thread.currentThread().getId() + ", get write lock success, stamp=" + stamp);
            try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
            try {
                x = 10;
                y = 10;
                System.out.println(LocalDateTime.now() + " Write Thread id#" + Thread.currentThread().getId() + ", x=" + x + ", y=" + y);
            } finally {
                stampedLock.unlockWrite(stamp);
            }
        }).start();

        new Thread(() -> {
            System.out.println(LocalDateTime.now() + " OptimisticRead Thread id#" + Thread.currentThread().getId() + ", begin to get OptimisticRead");
            // 获得一个乐观读的标记(不是锁)
            long stamp = stampedLock.tryOptimisticRead();
            int curX = x;
            int curY = y;
            // 检查乐观读标记是否被其他写锁更改
            if (!stampedLock.validate(stamp)) {
                System.out.println(LocalDateTime.now() + " OptimisticRead Thread id#" + Thread.currentThread().getId() + ", get OptimisticRead failed, stamp=" + stamp);

                // 获取一个悲观读锁
                stamp = stampedLock.readLock();
                try {
                    curX = x;
                    curY = y;
                    System.out.println(LocalDateTime.now() + " OptimisticRead transfer to read lock Thread id#" + Thread.currentThread().getId() + ", curX=" + curX + ", curY=" + curY);
                } finally {
                    stampedLock.unlockRead(stamp);
                }
            } else {
                System.out.println(LocalDateTime.now() + " OptimisticRead Thread id#" + Thread.currentThread().getId() + ", get OptimisticRead success, stamp=" + stamp + ", curX=" + curX + ", curY=" + curY);
            }
        }).start();

        new Thread(() -> {
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(LocalDateTime.now() + " Try Read Thread id#" + Thread.currentThread().getId() + ", begin to get try read lock");
            long stamp = stampedLock.tryReadLock();
            if (stampedLock.validate(stamp)) {
                System.out.println(LocalDateTime.now() + " Try Read Thread id#" + Thread.currentThread().getId() + ", get try read lock success, stamp=" + stamp);
                try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
                try {
                    System.out.println(LocalDateTime.now() + " Try Read Thread id#" + Thread.currentThread().getId() + ", x=" + x + ", y=" + y);
                } finally {
                    stampedLock.unlockRead(stamp);
                }
            } else {
                System.out.println(LocalDateTime.now() + " Try Read Thread id#" + Thread.currentThread().getId() + ", get try read lock failed, stamp=" + stamp);
            }
        }).start();
    }

}

输出:

2021-03-18T13:03:00.888 OptimisticRead Thread id#14, begin to get OptimisticRead
2021-03-18T13:03:00.889 OptimisticRead Thread id#14, get OptimisticRead success, stamp=256, curX=0, curY=0

2021-03-18T13:03:01.867 Write Thread id#13, begin to get write lock
2021-03-18T13:03:01.867 Read Thread id#12, begin to get read lock
2021-03-18T13:03:01.867 Write Thread id#13, get write lock success, stamp=384

2021-03-18T13:03:02.867 Try Read Thread id#15, begin to get try read lock
2021-03-18T13:03:02.867 Try Read Thread id#15, get try read lock failed, stamp=0

2021-03-18T13:03:11.867 Write Thread id#13, x=10, y=10
2021-03-18T13:03:11.867 Read Thread id#12, get read lock success, stamp=513
2021-03-18T13:03:11.867 Read Thread id#12, x=10, y=10

不可重入

如果再加一个线程,获取第一把悲观读锁后,再第一把锁未释放时,再获取第二把悲观读锁,导致所有线程阻塞。

new Thread(() -> {
    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
    long stamp = stampedLock.readLock();
    try {
        System.out.println("readLock是否可重入?");
        try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        long stamp2 = stampedLock.readLock();
        try {
            System.out.println("readLock可重入!");
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
        } finally {
            stampedLock.unlockRead(stamp2);
        }
    } finally {
        stampedLock.unlockRead(stamp);
    }
}).start();

程序将无法执行下去,一直停在此处,输出如下:

2021-03-18T13:18:17.644 OptimisticRead Thread id#14, begin to get OptimisticRead
2021-03-18T13:18:17.644 OptimisticRead Thread id#14, get OptimisticRead success, stamp=256, curX=0, curY=0
readLock是否可重入?
2021-03-18T13:18:18.623 Write Thread id#13, begin to get write lock
2021-03-18T13:18:18.623 Read Thread id#12, begin to get read lock

2021-03-18T13:18:19.624 Try Read Thread id#15, begin to get try read lock
2021-03-18T13:18:19.624 Try Read Thread id#15, get try read lock success, stamp=258

2021-03-18T13:18:24.626 Try Read Thread id#15, x=0, y=0

原理

https://segmentfault.com/a/1190000015808032

阅读 2615 · 发布于 2021-04-09

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批