ReentrantLock的底层实现机制 AQS

ReentrantLock的底层实现机制是AQS(Abstract Queued Synchronizer 抽象队列同步器)。AQS没有锁之类的概念,它有个state变量,是个int类型,为了好理解,可以把state当成锁,AQS围绕state提供两种基本操作“获取”和“释放”,有条双向队列存放阻塞的等待线程。AQS的功能可以分为独占和共享,ReentrantLock实现了独占功能(每次只能有一个线程能持有锁)。

在ReentrantLock类中,有一个内部类Sync,它继承了AQS,但是将lock()方法定义为抽象方法,由子类负责实现(采用的是模板方法的设计模式)。

abstract void lock();

Sync分为公平锁和非公平锁,所以又有FairSync和NonfairSync继承Sync。

ReentrantLock的默认构造实现是非公平锁,也就是线程获取锁的顺序和调用lock的顺序无关。所有线程同时去竞争锁,线程发出请求后立即尝试获取锁,如果有可用的则直接获取锁,失败才进入等待队列。

ReentrantLock的底层实现机制 AQS

在非公平锁NonfairSync中,

ReentrantLock的底层实现机制 AQS

线程只要有机会就抢占,才不管排队的事。直接尝试将state修改为1,如果修改state失败,则和公平锁一样,调用acquire。(乐观锁的思想)

在公平锁FairSync中,线程去竞争一个锁,可能成功也可能失败。成功就直接持有资源,不需要进入队列;失败的话进入队列阻塞,等待唤醒后再尝试竞争锁。

ReentrantLock的底层实现机制 AQS

首先尝试着去获取锁,如果state的当前状态为0,且没有前继线程在等待,表明没有线程占用,此时可以获得锁,然后设置当前线程为独占线程,并返回true,此时不会调用acquireQueued()方法(&& : 只有当&&左边程序为真,才会执行&&右边的程序,否者不会执行后面的),且不会执行selfInterrupt()方法。

ReentrantLock的底层实现机制 AQS

相反,如果获取锁失败,则会执行

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

该首先会将当前线程包装成一个Node,插入到双向队列尾

ReentrantLock的底层实现机制 AQS

标记1判断该Node的前继节点p,如果前一个节点正好是head,表示自己排在第一位,可以马上调用tryAcquire尝试。如果获取成功就简单了,直接修改自己为head。这步是实现公平锁的核心。标记2是线程没有获取到锁的情况(当前线程没有排到第一位),这个时候,线程可能等着下一次获取,也可能不想要了,Node变量waitState描述了线程的等待状态。

ReentrantLock的底层实现机制 AQS

shouldParkAfterFailedAcquire()方法会根据前一个节点的waitStatus作出抉择,如果前节点状态是SIGNAL,则当前线程需要阻塞。

ReentrantLock的底层实现机制 AQS

如果线程需要阻塞,则由parkAndCheckInterrupt()方法进行操作。LockSupport和cas一样,最终使用UNSAFE调用Native方法实现线程阻塞(park和unpark方法作用类似于wait和notify)

ReentrantLock的底层实现机制 AQS

释放锁:

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

头节点是获取锁的线程,如果tryRelease()释放成功,会将头节点先移出队列,再通知后面的节点获取锁

ReentrantLock的底层实现机制 AQS

如果有后继节点,则唤醒线程,重新尝试去获取锁。

ReentrantLock的底层实现机制 AQS

上一篇:BZOJ 3931: [CQOI2015]网络吞吐量 最大流


下一篇:设置mysql的interactive_timeout和wait_timeout的值