zoukankan      html  css  js  c++  java
  • Java并发编程 ReentrantLock 源码分析

    ReentrantLock 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 这个类主要基于AQS(AbstractOwnableSynchronizer)封装的 公平与非公平锁。

    所谓公平锁就是指 在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程,换句话说也就是先被锁定的线程首先获得锁。 非公平锁正好相反,解锁时没有固定顺序。

     

    让我们边分析源代码边学习如何使用该类

    先来看一下构造参数,默认是非公平锁。

     1     /**
     2      * Creates an instance of {@code ReentrantLock}.
     3      * This is equivalent to using {@code ReentrantLock(false)}.
     4      */
     5     public ReentrantLock() {
     6         sync = new NonfairSync();
     7     }
     8 
     9     /**
    10      * Creates an instance of {@code ReentrantLock} with the
    11      * given fairness policy.
    12      *
    13      * @param fair {@code true} if this lock should use a fair ordering policy
    14      */
    15     public ReentrantLock(boolean fair) {
    16         sync = fair ? new FairSync() : new NonfairSync();
    17     }

    NonfairSync是非公平锁,我们先来看非公平锁,是一个内部类继承了Sync。

     1     /**
     2      * Sync object for non-fair locks
     3      */
     4     static final class NonfairSync extends Sync {
     5         private static final long serialVersionUID = 7316153563782823691L;
     6 
     7         /**
     8          * Performs lock.  Try immediate barge, backing up to normal
     9          * acquire on failure.
    10          */
    11         final void lock() {
    12             if (compareAndSetState(0, 1))
    13                 setExclusiveOwnerThread(Thread.currentThread());
    14             else
    15                 acquire(1);
    16         }
    17 
    18         protected final boolean tryAcquire(int acquires) {
    19             return nonfairTryAcquire(acquires);
    20         }
    21     }

    我们看到它继承了Sync,我们接着看这个类的源码。

     1     abstract static class Sync extends AbstractQueuedSynchronizer {
     2         private static final long serialVersionUID = -5179523762034025860L;
     3 
     4         abstract void lock();
     5 
     6         final boolean nonfairTryAcquire(int acquires) {
     7             final Thread current = Thread.currentThread();
     8             int c = getState();
     9             if (c == 0) {
    10                 if (compareAndSetState(0, acquires)) {
    11                     setExclusiveOwnerThread(current);
    12                     return true;
    13                 }
    14             }
    15             else if (current == getExclusiveOwnerThread()) {
    16                 int nextc = c + acquires;
    17                 if (nextc < 0) // overflow
    18                     throw new Error("Maximum lock count exceeded");
    19                 setState(nextc);
    20                 return true;
    21             }
    22             return false;
    23         }
    24 
    25         protected final boolean tryRelease(int releases) {
    26             int c = getState() - releases;
    27             if (Thread.currentThread() != getExclusiveOwnerThread())
    28                 throw new IllegalMonitorStateException();
    29             boolean free = false;
    30             if (c == 0) {
    31                 free = true;
    32                 setExclusiveOwnerThread(null);
    33             }
    34             setState(c);
    35             return free;
    36         }
    37     }

    Sync这个类与AbstractQueuedSynchronizer 一起完成了锁的逻辑,现在我们开始从头分析一个线程如何获取锁,以及获取不到锁时如何被阻塞。当用户调用lock方法获取锁的时候,首先会先通过compareAndSetState(NonfairSync第11行)来设置锁定状态,如果原先状态为0,则说明目前没有线程持有锁,那么设置状态为1,并且设置当前线程是当前拥有独占访问的线程(setExclusiveOwnerThread),那么另外一种情况就是compareAndSetState方法返回false,也就是说之前已经有线程持有锁,那么就会执行acquire方法(NonfairSync第15行),这个方法是AbstractQueuedSynchronizer里面的方法。

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

    首先调用了tryAcquire方法,这个类在子类当中实现,也就是上面NonfairSync第18行,实际上他调用了 nonfairTryAcquire,这个方法分两步,首先在判断一下状态(state)是否等于0,也就是重新尝试获取所,如果获取到锁则改变状态compareAndSetState 然后设置当前线程是当前拥有独占访问的线程(setExclusiveOwnerThread),跟上面讲到的一样。

    如果重新尝试获取所失败,则判断是不是当前线程重复加锁,如果是的话就把状态进行增加。

    如果上面都不是就返回FALSE, 如果返回FALSE 那么 acquire(int arg)方法的acquireQueued就会执行,这个方法会把不能获取锁的线程形成一个CHL队列保存起来,然后把线程阻塞。上面就基本讲完了

    线程如何获取锁, 获取到锁就把状态设置成1。

    如果是持有锁的线程继续调用 LOCK方法,那就把状态进行叠加。

    如果获取不到锁,那么AbstractQueuedSynchronizer 会把线程以一个CHL队列的形式保存起来,然后设置线程阻塞,等待释放。

    然后就是释放锁的操作

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

    这个release方法又是AbstractQueuedSynchronizer 里面提供的方法。

    1     public final boolean release(int arg) {
    2         if (tryRelease(arg)) {
    3             Node h = head;
    4             if (h != null && h.waitStatus != 0)
    5                 unparkSuccessor(h);
    6             return true;
    7         }
    8         return false;
    9     }

    首先在第2行先调用tryRelease尝试释放锁,这个方法是在ReentrantLock的内部类Sync当中重写的,释放成功会返回TRUE。然后调用unparkSuccessor来释放阻塞队列当中的线程,然后被唤醒的线程会继续获取锁,如此反复。

    最后我们再来看一下说明是公平锁FairSync。

     1     static final class FairSync extends Sync {
     2         private static final long serialVersionUID = -3000897897090466540L;
     3 
     4         final void lock() {
     5             acquire(1);
     6         }
     7 
     8         /**
     9          * Fair version of tryAcquire.  Don't grant access unless
    10          * recursive call or no waiters or is first.
    11          */
    12         protected final boolean tryAcquire(int acquires) {
    13             final Thread current = Thread.currentThread();
    14             int c = getState();
    15             if (c == 0) {
    16                 if (!hasQueuedPredecessors() &&
    17                     compareAndSetState(0, acquires)) {
    18                     setExclusiveOwnerThread(current);
    19                     return true;
    20                 }
    21             }
    22             else if (current == getExclusiveOwnerThread()) {
    23                 int nextc = c + acquires;
    24                 if (nextc < 0)
    25                     throw new Error("Maximum lock count exceeded");
    26                 setState(nextc);
    27                 return true;
    28             }
    29             return false;
    30         }
    31     }

    仔细观察你会发现它与非公平锁唯一的区别就是在tryAcquire这里方法里面。上面已经用红色标记上了,主要是多了这么一个条件,就体现了锁的公平性。

    当一个线程尝试获取锁时,那么先会判断当前有没有已经等待获取锁的线程队列,如果的话,按照公平原则,那么当前线程就会被加入阻塞队列的尾巴,如果是非公平锁,那么则不会判断。

    这个类的核心原理基本介绍完了,其实主要核心的东西是在AbstractQueuedSynchronizer这个类里。

    这里给大家提供一个关于AbstractQueuedSynchronizer的源码分析的文章:

    http://ifeve.com/introduce-abstractqueuedsynchronizer/ 大家可以结合着一起看一下。

  • 相关阅读:
    JZOJ 3845. 简单题(simple)
    JZOJ 3844. 统计损失(count)
    JZOJ 3843. 寻找羔羊(agnus)
    JZOJ 3833. 平坦的折线
    JZOJ 1956. 矩形
    JZOJ 3832. 在哪里建酿酒厂
    mysql 语法一 :case when详解
    阿里云推荐码
    redis配置文件详解(转)
    压力测试工具 webbench总结
  • 原文地址:https://www.cnblogs.com/daxin/p/3832419.html
Copyright © 2011-2022 走看看