J.U.C 学习【四】重入锁 -- ReentrantLock

文章中的源码均来自JDK1.8

1. 前言

     ReentrantLock, 顾名思义就是支持重进入的锁,也就是说一个线程可以对资源重复加锁。除了支持重进入外,ReetrantLock还支持公平锁和非公平锁。

2. 重进入分析

     在J.U.C 学习【二】AbstractQueuedSynchronizer独占模式和示例中的示例(ExclusiveLock), 如果这个示例遇到如下场景:当一个线程调用了它的lock()方法后,重新在调用一次lock()方法,它将会给自己阻塞住,原因是在重写的tryAcquire的方法中没有考虑到重进入这个场景,所以ExclisiveLock是不支持重进入的锁,然而这个场景很常见,比如递归。对于这种场景,我们可以使用支持重进入的ReetrantLock,下面来看看它是如何实现重进入的

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

上面代码是ReentrantLock的内部类Sync中的方法,从代码中可以看到,当同步状态state == 0时会去判断当前线程是不是已经获取到同步状态的线程,如果是则将同步状态增加并返回true,而不是直接返回false。下面来看看释放同步状态的代码

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

如果该锁给锁了n次,那么tryRelease方法前 (n - 1) 次都是返回false,说明同步状态还没完全释放掉,只有当同步状态为0时,才将占有同步状态的线程设置为null并返回true。

2. 公平锁和非公平锁

    前面又说到ReentrantLock有一个内部类叫Sync,除此之外还有两个内部类,分别是NonfairSync和FairSync,非公平锁和公平锁,这两个类继承了Sync,分别重写了tryAcquire方法,NonfairSync在tryAcquire方法中调用的是nonfairTryAcquire方法,也就是将冲入锁所展示的代码,该方法如果当新来的线程尝试获取同步状态时不用去判断是否有线程在它之前申请过同步状态,只要设置state成功即可获取到同步状态。而公平锁不同,下面来看看公平锁的实现

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

公平锁的实现比非公平锁多调用了一个hasQueuedPredecessors()方法,当该方法返回true时说明有线程比当前线程更早地请求过锁,需要等待前面的线程释放锁后才能继续去获取锁。


参考资料:《Java并发编程的艺术》

猜你喜欢

转载自blog.csdn.net/qq_36712034/article/details/80724433