咱们能够理解到它是一个可重入锁,上面咱们就一起看一下它的底层实现~

构造函数

咱们在应用的时候,都是先new它,所以咱们先看下它的构造函数,它次要有两个:

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

从字面上看,它们之间的不同点在于fair,翻译过去就是偏心的意思,大体能够猜到它是用来构建偏心锁和非偏心锁,在持续往下看源码之前,先给大家科普一下这两种锁。

偏心锁 & 非偏心锁

  • 偏心锁 多个线程依照申请锁的程序去取得锁,线程会间接进入队列去排队,永远都是队列的第一位能力失去锁。(例如银行办业务取号)

这种锁的长处很显著,每个线程都可能获取资源,毛病也很显著,如果某个线程阻塞了,其它线程也会阻塞,然而cpu唤醒开销很大,之前也给大家讲过

  • 非偏心锁 多个线程都去尝试获取锁,获取不到就进入期待队列,cpu也不必去唤醒

优缺点正好和上边相同,长处缩小开销,毛病也很显著,可能会导致始终获取不到锁或长时间获取不到锁

好,有了基本概念之后,咱们持续往下看

NonfairSync

首先,咱们看下非偏心锁,默认状况下,咱们申请的都是非偏心锁,也就是new ReentrantLock(),咱们接着看源码

static final class NonfairSync extends Sync {    private static final long serialVersionUID = 7316153563782823691L;    /**        * Performs lock.  Try immediate barge, backing up to normal        * acquire on failure.        */    final void lock() {        if (compareAndSetState(0, 1))            setExclusiveOwnerThread(Thread.currentThread());        else            acquire(1);    }    protected final boolean tryAcquire(int acquires) {        return nonfairTryAcquire(acquires);    }}

它继承了Sync,Sync是一个内容动态抽象类:

abstract static class Sync extends AbstractQueuedSynchronizer {...}

分为偏心和非偏心,应用AQS状态来示意持锁的次数,在构造函数初始化的时候都有sync = ...,咱们接着看NonfairSync。在应用的时候,咱们调用了lock.lock()办法,它是ReentrantLock的一个实例办法

 // 获取锁 public void lock() {        sync.lock();    }

实际上外部还是调了sync的外部办法,因为咱们申请的是非偏心锁,所以咱们看NonfairSync下的lock实现:

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

compareAndSetState这个办法,是AQS的外部办法,意思是如果以后状态值等于预期值,则主动将同步状态设置为给定的更新值。此操作具备volatile读写的内存语义。

protected final boolean compareAndSetState(int expect, int update) {    // See below for intrinsics setup to support this    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);}

能够看到执行lock办法,会通过AQS机制计数,setExclusiveOwnerThread设置线程独占拜访权限,它是AbstractOwnableSynchronizer的一个外部办法,子类通过应用它来治理线程独占

public abstract class AbstractQueuedSynchronizer    extends AbstractOwnableSynchronizer    implements java.io.Serializable {}

能够看到它是继承了AbstractOwnableSynchronizer。上面接着看,咱们说如果理论值等于期望值会执行上边的办法,不冀望的时候会执行acquire(1)

public final void acquire(int arg) {    if (!tryAcquire(arg) &&        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))        selfInterrupt();}

这个办法以独占模式获取,疏忽中断,它会尝试调用tryAcquire,胜利会返回,不胜利进入线程排队,能够反复阻塞和解除阻塞。看下AQS 外部的这个办法

protected boolean tryAcquire(int arg) {    throw new UnsupportedOperationException();}

咱们能够看到实现必定不在这,它的具体实现在NonfairSync

protected final boolean tryAcquire(int acquires) {            return nonfairTryAcquire(acquires);        }

能够看到它调用了,nonfairTryAcquire办法,这个办法是不偏心的tryLock,具体实现在Sync外部,这里咱们要重点关注一下

final boolean nonfairTryAcquire(int acquires) {    final Thread current = Thread.currentThread();    // 返回同步状态值,它是AQS外部的一个办法     //  private volatile int state;    // protected final int getState() {    //     return state;    // }    int c = getState();    if (c == 0) {        // 为0就比拟一下,如果与期望值雷同就设置为独占线程,阐明锁曾经拿到了        if (compareAndSetState(0, acquires)) {            setExclusiveOwnerThread(current);            return true;        }    }    // 否则 判断如果以后线程曾经是被设置独占线程了    else if (current == getExclusiveOwnerThread()) {        // 设置以后线程状态值 + 1 并返回胜利        int nextc = c + acquires;        if (nextc < 0) // overflow            throw new Error("Maximum lock count exceeded");        setState(nextc);        return true;    }    // 否则返回失败 没拿到锁    return false;}

好,咱们再回过头看下 acquire

 public final void acquire(int arg) {         // 如果以后线程没有获取到锁 并且 在队列中的线程尝试一直拿锁如果被打断了会返回true, 就会调用 selfInterrupt           if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }selfInterrupt很好了解,线程中断 static void selfInterrupt() {        Thread.currentThread().interrupt();    }

其实咱们关注的重点是这个办法acquireQueued,首先关注一下入参,它外部传入了一个addWaiter,最初它回NODE节点

 private Node addWaiter(Node mode) {       // mode 没啥好说的就是一个标记,用于标记独占模式   static final Node EXCLUSIVE = null;        Node node = new Node(Thread.currentThread(), mode);        // Try the fast path of enq; backup to full enq on failure        Node pred = tail;        if (pred != null) {            node.prev = pred;            if (compareAndSetTail(pred, node)) {                pred.next = node;                return node;            }        }        enq(node);        return node;    }

咱们能够大体从猜到,Node是一个期待队列的节点类,是一个链表构造,之前咱们讲FutureTask源码的时候也遇到过这种构造,它通常用于自旋锁,在这个中央,它是用于阻塞同步器

     +------+  prev +-----+       +-----+head |      | <---- |     | <---- |     |  tail     +------+       +-----+       +-----+

好,上面咱们关注一下 acquireQueued

final boolean acquireQueued(final Node node, int arg) {    boolean failed = true;    try {        // 默认是 false        boolean interrupted = false;        // 进入阻塞循环遍历 线程队列        for (;;) {            // 返回前一个节点            final Node p = node.predecessor();            // 判断如果前一个节点是头部节点,并且拿到锁了,就会设置以后节点为头部节点            if (p == head && tryAcquire(arg)) {                setHead(node);                // 这里能够看到正文 help gc ,                p.next = null; // help GC                failed = false;                return interrupted;            }            // 查看并更新未能获取的节点的状态。如果线程应该阻塞,则返回 true 并且线程中断了            if (shouldParkAfterFailedAcquire(p, node) &&                parkAndCheckInterrupt())                interrupted = true;        }    } finally {        // 如果失败  勾销正在尝试获取的节点        if (failed)            cancelAcquire(node);    }}

从下面的源码来看,在领会一下下面讲的非偏心锁的概念,是不是更好了解一些,而后就是开释锁unlock,这个办法咱们能够看到是ReentrantLock下的一个实例办法,所以偏心锁的开释锁也是调的这个办法,其实最终能够猜到调用的还是sync的办法

public void unlock() {        sync.release(1);    }Sync继承AQS,release是AQS的外部办法 public final boolean release(int arg) {       // 尝试开释锁  tryRelease 在Sync外部        if (tryRelease(arg)) {            Node h = head;            // 如果节点存在 并且状态值不为0            if (h != null && h.waitStatus != 0)                // 唤醒下个节点                unparkSuccessor(h);            return true;        }        return false;    }private void unparkSuccessor(Node node) {             int ws = node.waitStatus;        if (ws < 0)            compareAndSetWaitStatus(node, ws, 0);        Node s = node.next;        if (s == null || s.waitStatus > 0) {            s = null;            for (Node t = tail; t != null && t != node; t = t.prev)                if (t.waitStatus <= 0)                    s = t;        }        if (s != null)            // 能够看到调用了 LockSupport来唤醒            LockSupport.unpark(s.thread);    }

咱们再看下tryRelease, 同样这个实现在Sync内

 protected final boolean tryRelease(int releases) {            // 同样开释锁的时候 仍然应用 AQS计数            int c = getState() - releases;            // 判断以后线程是否是独占线程,不是抛出异样            if (Thread.currentThread() != getExclusiveOwnerThread())                throw new IllegalMonitorStateException();            boolean free = false;            // 如果是0 示意是开释胜利            if (c == 0) {                free = true;                // 并且把独占线程设为null                setExclusiveOwnerThread(null);            }            // 更新状态值            setState(c);            return free;        }

FairSync

偏心锁FairSync的区别在于,它的获取锁的实现在它的外部,Sync默认外部实现了非偏心锁

static final class FairSync extends Sync {        private static final long serialVersionUID = -3000897897090466540L;        // 这个办法最终调用 tryAcquire        final void lock() {            acquire(1);        }        // 偏心锁的实现        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;        }    }

它的实现比较简单,通过实现能够发现,它依照申请锁的程序来获取锁,排第一的先拿到锁,在联合下面的概念了解一下,就很好了解了.

开释锁unlock,下面咱们曾经讲过了~

结束语

本节内容可能有点多,次要是看源码,能够打断点本人调一下, 触类旁通,通过源码去了解一下什么是偏心锁和非偏心锁, ReentrantLock可重入锁体验在哪里。