可重入锁ReentrantLock的实现原理

ReentrantLock是Lock的默认实现,在了解ReentrantLock之前,我觉得有必要先看一下AQS框架分析condition,ReentrantLock继承自AQS,只不过子类重写了AQS的部分方法。


构造方法

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

默认的构造器创建一个NonfairSync对象,就是非公平锁,有参的构造可以指定使用公平锁还是非公平锁。可以看到不管是NonfairSync还是FairSync都继承了Sync类,Sync又继承了AQS类,它在AQS的基础上又进行了扩展,感觉使用了典型的模板方式模式。

NonfairSync

首先看一下非公平锁的获取和释放。

1.1 lock方法

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

0表示当前是自由状态,1表示当前已获取到锁,使用CAS把当前线程的状态由0设为1,并把当前线程设为独占模式。多个线程只能有一个线程CAS成功,其它失败的线程就进入acquire方法中,acquire在AQS类中,不在多说,但是acquire中的tryacquire方法是在Sync中被重写。

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
        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;
        }

检查state是否为0,如果state是0,使用CAS把state设为1,并把当前线程设为独占模式,直接返回true,表示已经获取到锁。如果state不为0,检查一下当前线程如果是独占线程,更新自己的state,表示重入的次数。其他情况就是获取锁失败,直接返回false。

1.2  unlock方法

    public void unlock() {
        sync.release(1);
    }

释放锁的代码就简单多了,只有一句代码,直接调用release方法,具体还是看AQS分析,但是在Sync中重写了tryRelease方法。

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;
        }

这里入参是1,如果当前线程不是独占线程,直接抛出异常。计算如果释放后的state如果为0,表示成功释放锁,把独占线程设为null,更新state,直接返回true。


FairSync

公平锁与非公平锁的区别在于公平锁在获取锁的时候直接执行acquire方法,不会检查state。但在acquire中tryacquire方法是在fairSync中实现的。

        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;
        }

这个方法和非公平锁中的nonfairtryacquire方法很相似,不同处在于当state是0的时候,调用hasQueuedPredecessors方法。

    public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
通过判断当前线程是否在同步队列的头节点,返回是否有比当前线程等待更久的线程。后进来的线程总是进入同步队列等待,会导致效率降低。所以大多数情况下还是使用非公平锁。


总结

ReentrantLock是一个用代码控制的锁,相比与synchronized,它提供了更细的锁粒度,而且提供了可中断锁,超时锁。synchronized就不能中断正在等待锁的线程,这是相比synchronized的优点。但是使用ReentrantLock必须手动释放锁。在jdk 1.8中ConcurrentHashMap已经使用了synchronized,说明synchronized已经得到了极大的优化。除非有明确的需要,否则一般推荐使用synchronized。

AQS是并发包的核心,真正理解了AQS实现原理,在看ReentrantLock就简单多了。这里不得不说一下Doug lea大神深厚的功力,用简洁的代码写出了复杂的逻辑。在工作中我们还是要花些时间来提高自己的java基本功。





猜你喜欢

转载自blog.csdn.net/qq_30572275/article/details/80373097
今日推荐