ReentrantLock你知道是怎么实现的么?

341 阅读2分钟

ReentrantLock

基本介绍引言

Lock在JavaSe5之后出现,相比于Synchronized拥有锁获取和释放的可操作性、可中断等同步特性。Synchronized是基于进入和退出monitor对象来实现方法和代码的同步的(monitorenter和monitorexit),那Lock又是基于什么是实现的呢?没错就是大家经常听到的AQS(同步队列器). 首先我们可以看下current包下面的ReentrantLock的一个类图

微信图片_20210313164734.png

ReentrantLock的整个类结构比较简单,实现了Lock接口,内部还组合了内部类Sync以及Sync两个子类。NonfairSync和FairSync.Lock接口的实现是ReentrantLock的对外调用能力的封装,而具体的实现则是依赖内部类Sync的父类AbstractQueuedSynchronizer提供的封装。我们先来看看Sync的一个类结构图

Sync类图.png ReentrantLock对Lock接口的实现实际上则又完全是对内部类Sync的方法的一个调用譬如

    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
        public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
        public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

FairSync和NonFairSync

在讲AQS之前我们先简单了解一下ReentrantLock的公平锁和非公平锁的区别,ReentrantLock提供了两个构造方法

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

FairSync和NonFairSync的代码如下

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

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

可以看出不管是lock还是tryAcquire方法,非公平锁相对于公平锁实际上都只多了一个步骤,就是一上来就去申请锁直接CAS,而公平锁是直接进入FIFO队列有序获取,非公平锁当一进入申请锁失败之后就和公平锁一样了,这个里面没有unlock也说明了unlock两个锁之间并无区别。

Sync

接下来我们看看Lock接口的是怎么被Sync实现的这里我们就先挑几个核心的方法

  abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        //这个lock方法由前面两个子类实现
        abstract void lock();
        //被AQS的模板方法release调用
        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;
        }
        //非公平锁的tryAcquire直接调用此方法
         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;
        }
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
        }
        
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    //ReentrantLock的unlock直接调用这个方法
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    }

可以看出Sync的方法实现基本就是对父类AQS方法的一个按需调用,我们自己写一个锁也是一样只要去按需实现AQS里面的一些protected方法比如上面的tryAcquire。

AQS

到这我们可以看看AQS为我们锁的实现提供了一些什么东西,如果我们不依赖AQS的话你会想到需要什么?
1.一个state变量,记录锁的状态,那么它至少要有0,1两个状态。同时对state操作要保证线程安全,也就是需要CAS
2.需要记录持有当前锁的线程
3.需要底层支持对线程的阻塞和唤醒操作
4.需要有一个队列维护所有的阻塞线程,该队列也必须线程安全也需要支持CAS
那我们再看看AQS里面对应的一些属性和方法吧。

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    static final class Node {
         ........暂时不表.......
    }
    //队列头节点
    private transient volatile Node head;
    //队列尾结点
    private transient volatile Node tail;
    //state的取值不仅可以是0,1还可以大于1,这样就可以支持可重入锁
    private volatile int state;
    }
    //state的CAS
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
    //头节点CAS
    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }
    //尾节点CAS
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }
    }
    /**
     *AQS的的阻塞唤醒则用了LockSupport封装的Unsafe类中的park/unpark,
     *当调用park线程阻塞,另一个线程调用unpark(Thread t),被传入的阻塞线程就可以被精准唤醒
     */
    public class LockSupport {
      public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
      }
        public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
      }
    }

具体的AQS实现细节在此先不细讲,后面有时间再开一篇文章,接下来我们再开看看Lock里面的Condition是个什么?

Condition

开头我们说过Synchronized是依靠进出对应的monitor对象来实现的,同时wait(),notify等方法存在可以实现等待/通知模式,Condition接口也提供了类似Object上的这几个方法,与Lock配合可以实现等待/通知模式。Condition必须通过Lock的newCondition()方法获取。

public interface Condition {
    void await() throws InterruptedException;
    void awaitUninterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}

AQS内部类ConditionObject实现了Condition接口,并且维护了一个等待队列,节点类型复用了AQS中的节点定义

 public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
        /**
        *使当前线程进入等待队列并释放锁,状态变为等待状态。将AQS中的首
        *节点加入到Condition的等待队列
        */
        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            //当前线程加入等待队列
            Node node = addConditionWaiter();
            //释放锁
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            //判断是否存在于AQS队列中,只有被unpark唤醒的才会加入,中断的不会
            while (!isOnSyncQueue(node)) {
            //自己阻塞自己
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            //重新拿锁
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
        
          public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
        //唤醒同步队列的第一个线程
        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
        
        final boolean transferForSignal(Node node) {
            if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
                return false;
            //先将node放入AQS同步队列中,再调用unpark
            Node p = enq(node);
            int ws = p.waitStatus;
            if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
                LockSupport.unpark(node.thread);
            return true;
    }
       ..................其他方法实现暂时省 ...............     
       }

conditionObject其他方法也类似,接下来我们可以看下condition的实际应用ArrayBlockingQueue

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
      /** The queued items */
    final Object[] items;
    
    final ReentrantLock lock;
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;
    
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
    
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();//put时候队列满了则阻塞于非满条件
            enqueue(e);
        } finally {
            lock.unlock();
        }
      }
    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();//put成功通知非空条件
    }
      public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();//队列为空的时候,take阻塞于非空条件
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
        }

可以看出通过Condition我们可以很好的实现阻塞队列。