深刻浅出ReentrantLock源码解析

ReentrantLock不可是可重入锁,并且仍是公平或非公平锁,在工做中会常用到,将本身对这两种锁的理解记录下来,但愿对你们有帮助。html

前提条件

在理解ReentrantLock时须要具有一些基本的知识java

理解AQS的实现原理

以前有写过一篇《深刻浅出AQS源码解析》关于AQS的文章,对AQS原理不了解的同窗能够先看一下ui

什么是可重入锁

当一个线程已经持有锁,若是该如今再次获取锁,是否能够获取成功?若是能获取成功则说明该锁是可重入的,不然是不可重入的线程

什么是公平锁和非公平锁

公平与非公平的一个很本质的区别就是,是否遵照FIFO(也就是先来后到)。当有多个线程来申请锁的时候,是否先申请的线程先获取锁,后申请的线程后获取锁?若是是的,则是公平锁,不然是非公平锁code

更准确地说,先申请锁的线程先得到锁竞争的权利。对于公平的排他锁而言,先申请锁的线程会先获取锁,可是对于公平的共享锁而言,先申请锁的线程会先拥有获取锁竞争的权利,其余等待共享锁的线程也会被唤醒,有可能后唤醒的线程先获取锁。htm

ReentrantLock 源码解析

ReentrantLock的功能主要是经过3个内部类SyncFairSyncNonfairSync来实现的,这3个内部类继承了AbstractQueuedSynchronizer,其中FairSyncNonfairSync类继承了Sync,接下来咱们一一解读这几个内部类。blog

ReentrantLock.Sync类源码解析

因为ReentrantLock.Sync类中的核心代码比较少,原理也比较简单,因此就直接在代码中经过详细注释的方式来解读继承

abstract static class Sync extends AbstractQueuedSynchronizer {

    /**
     * 定义了一个抽象方法,用来获取锁
     */
    abstract void lock();

    /**
     * NonfairSync中tryAcquire和、ReentrantLock.tryLock会使用到
     * 重要功能:快速尝试获取锁,若是可以获取锁返回true,不然返回false
     * 在尝试获取锁的过程当中,不会阻塞当前线程,通常状况下是当前线程已经持有锁时
     * 才有多是能够直接获取锁,这也是可重入功能的核心实现
     */
    final boolean nonfairTryAcquire(int acquires) {
        // 获取当前线程
        final Thread current = Thread.currentThread();
        /**
         * state是AQS对外提供的一个变量,让不一样的实现类能够经过这个变量
         * 来控制锁被线程获取锁的次数
         */
        int c = getState();
        // 当state为0表示该锁是没有被任何线程持有
        if (c == 0) {
           /**
            * CAS操做若是成功,说明当前线程竞争到了锁资源,
            * 不然被其余线程竞争到了,当前线程须要进入AQS的同步队列
            * 对于尝试修改state的值的线程能够同时是多个,
            * 他们之间没有前后顺序,这也是非公平的重要体现
            */
            if (compareAndSetState(0, acquires)) {
              /**
                * 当前线程已经持有锁了,设置锁的占有者
                */
                setExclusiveOwnerThread(current);
                return true;
            }
        }
      /**
        * 若是持有锁的线程是当前线程,能够继续尝试获取锁
        * 这也是可重入的重要体现
        */
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
           /**
            * state是int类型,也就是可重入次数不能低于Integer.MAX_VALUE
            */
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
           /**
            * 获取锁之后直接设置state的值
            */
            setState(nextc);
            return true;
        }
       /**
        * 若是一个线程既不是第一次获取锁,又不是已经获取锁,
        * 则该线程没法获取锁,须要进入AQS的同步队列排队
        */
        return false;
    }

    protected final boolean tryRelease(int releases) {
       /**
        * 计算释放releases个资源后state的值
        */
        int c = getState() - releases;
       /**
        * 持有锁的线程若是不是当前线程,没法释放资源
        */
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
       /**
        * 当全部的资源所有释放掉(c=0)时,锁的持有者须要设置为null,
        * 让后续线程能够来竞争锁
        */
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
       /**
        * 修改state的状态
        */
        setState(c);
        return free;
    }

    protected final boolean isHeldExclusively() {
        /**
        * 当前线程是否持有锁
        */
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

ReentrantLock.NonfairSync类源码解析

static final class NonfairSync extends Sync {
    /**
     * 非公平锁,对外获取锁的步骤:
     * 首先,尝试修改state的状态(从0修改为1),若是修改为功说明当前没有任何线程持有锁
     * 若是线程获取到锁,则把锁的持有线程设置为当前线程
     * 若是没法获取锁,说明锁已经被线程持有,有两种状况:
     * 状况1:持有锁的线程是当前线程,能够走可重入的流程
     * 状况2:持有锁的线程不是当前线程,须要进入AQS去排队
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    /**
     * 尝试快速获取锁
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

ReentrantLock.FairSync类源码解析

static final class FairSync extends Sync {
    /**
     * 阻塞方式获取锁
     */
    final void lock() {
        acquire(1);
    }

    /**
     * 尝试获取公平锁,与上面分析的nonfairTryAcquire方法很相似,
     * 重点描述彼此之间的区别
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            /**
             * 公平锁与非公平锁很大的一个区别是:
             * 在尝试获取锁的时候,若是AQS的同步队列中有其余线程在等待获取锁
             * 则尝试获取锁失败,须要进入AQS的同步队列排队
             * hasQueuedPredecessors方法判断AQS的同步队列是否有线程在等待
             */
            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;
    }
}

ReentrantLock类源码解析

ReentrantLock类的实现方式比较简单,主要是依靠NonfairSyncFairSync实现的功能队列

public class ReentrantLock implements Lock, java.io.Serializable {

    private final Sync sync;

    /**
     * 默认是非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 获取锁,获取的时候申请1个资源
     */
    public void lock() {
        sync.lock();
    }

    /**
     * 可中断的方式获取锁
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     * 
     * 尝试获取锁,公平锁和非公平锁都是直接去尝试获取锁
     * 通常在使用该方法的时候,若是尝试获取锁失败,会有后续操做,
     * 多是直接调用lock以阻塞的方式来获取锁
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     * 带有超时时间的方式尝试获取锁
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    /**
     * 释放锁,释放掉1个资源
     */
    public void unlock() {
        sync.release(1);
    }
}

总结

  • 对于已经持有锁的线程,优先申请到资源
  • 对与没有持有锁的线程,须要等待持有锁的线程释放掉全部资源,包括可重入时申请到的资源
  • 公平锁在申请资源的时候要先检查AQS同步队列中是否有等待的线程,也就线程获取锁是按照FIFO的方式
相关文章
相关标签/搜索