ReentrantLock源码

概述

  AQS框架下的锁都是实现Lock接口并实现tryAcquire方法,在tryAcquire方法中对state变量进行修改来改变锁的状态。

  • 重入性。获得锁的线程可以再次获得锁。
  • 公平锁。严格保证先尝试获得锁的线程能够先获得锁。
  • 非公平锁。不能严格保证先尝试获得锁的线程先获得锁。

获得锁

非公平锁

  cas修改state的状态,如果成功设置当前重入锁的拥有者是当前线程。cas对当前state的期待值为0,所以一个线程如果是重入自己持有的锁,在这一步是无法成功的。

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

  如果cas失败会调用acquire(1),即调用tryAcquire(1),最终调用的是nonfairAcquire(1),该方法是非公平锁的核心实现。

  • 如果锁的state=0,cas修改,如果成功代表获得锁成功返回true;失败返回false,进入AQS的队列。
  • 如果state!=0但当前锁的线程拥有者是当前线程,进入锁的重入阶段,修改锁的state,记录所的重入次数,返回true代表线程成功获得锁。

  非公平重入锁的tryAcquire实现相对简单

  • 在锁的内部记录锁的持有线程,方便锁的重入
  • 用state的值记录锁的重入次数
 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
 /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        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;
        }

公平锁

  公平锁的lock方法中直接调用了acquire,而非公平锁lock方法中先cas,cas失败后才调用的acquire。这是公平性和非公平性的体现,acquire失败后会进入队列,AQS队列中的线程只有pre是head才可以尝试获得锁,即aqs通过队列的方式维护了公平性,如果把所有锁的获得都交给aqs去管理,那么锁就是公平的。相应的如果在加入aqs之前先自己尝试获取锁,那么这个锁就未必是公平的。例如:A线程获取锁的时候发现state不是0,失败进入AQS队列,B线程尝试获取锁的时候state恰好变成0,由于A线程在AQS队列尾部,A线程不具有争取锁的资格,所以B线程获得锁A没有获得锁,这就是非公平性的体现。  

 final void lock() {
            acquire(1);
        }

  这里同样分为两个部分

  • state==0代表锁空闲,但是要先判断AQS里是否有前继节点,如果有代表已经有人在排队,返回false进入AQS排队
  • 如果锁的持有着是当前线程,则重入并修改state
 /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
        }

课代表划重点非公平锁和公平锁

  • 实现

  1. 非公平锁在lock方法中会先进行一次cas,cas成功当前线程就获取到锁,失败后才调用tryAcquire。公平锁没有先调用cas,而是调用acquire即调用tryAcquire。
  2. 在tryAcquire中,非公平锁只要当前state=0就可以进行cas,公平锁在state=0的时候还要先判断队列里是否有等待的线程,只有队列中没有等待的线程的情况下才会cas。
  3. 无论是公平锁还是非公平锁,才tryAcquire失败进入AQS队列后,AQS的先入先出的特性能够保证后续的公平性。
  • 比较

  1. 非公平锁容易出现饥饿。一个刚刚释放锁的线程再次获取锁的概率很大,所以会出现一个线程连续多次获得锁,造成饥饿。
  2. 公平锁效率低。公平性需要线程切换从而有许多没有必要的切换。

  

释放锁

  公平锁和非公平锁的释放没有区别,都是静态内部类sync中的tryRelease方法。

  • 如果当前线程不是锁的持有线程,抛出异常。
  • 由于释放锁的线程一定是持有锁的线程,所以释放锁修改state不需要cas,直接相减。
  • 如果释放完毕后state=0,返回true,并设置当前所的持有者为null。
  • 如果释放完毕后state=0,返回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;
        }

  

其余获得锁的方法

  这些获得锁的方法都是公平锁和非公平锁没有区别。

lockInterruptibly

  在获得锁失败被park时候能够相应中断,实现比较简单,直接调用AQS里的方法。

tryLock

  非阻塞获取锁,类似NIO。如果lock没有被别的线程持有,那么tryLock会立即返回true,这种行为其实是一种非公平的锁,另外从它调用的方法nonfairTryAcquire也可以看出这一点。这也就意味着如果你申请了一个公平锁,调用一个公平锁的tryLock它的行为也是非公平的。如果lock此时被别的线程持有,tryLock会返回false。

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
        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;
        }
原文地址:https://www.cnblogs.com/AshOfTime/p/10890108.html