从AbstractQueuedSynchronizer到Reentrantlock。
前言
谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)!
本文基于JDK1.8来解读从AQS到Reentrantlock源码。
什么是AQS
AQS是AbustactQueuedSynchronizer的简称,它是一个Java提高的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,例如ReentrantLock,CountdowLatch就是基于AQS实现的,用法是通过继承AQS实现其模版方法,然后将子类作为同步组件的内部类。
AQS结构
我们先来看看AQS有哪些属性。
//队列头节点
private transient volatile Node head;
//队列尾节点
private transient volatile Node tail;
//代表共享资源
private volatile int state;
比较重要的是volatile这个关键字,它保证了线程之前的可见性和有序性,如对此不了解的同学可以去查一下相关资料。
如上图,AQS维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。
Node结构
/** Marker to indicate a node is waiting in shared mode */
// 标识节点当前在共享模式下
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
// 标识节点当前在独占模式下
static final Node EXCLUSIVE = null;
// ======== 下面的几个int常量是给waitStatus用的 ===========
/** waitStatus value to indicate thread has cancelled */
// 代码此线程取消了争抢这个锁
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
// 官方的描述是,其表示当前node的后继节点对应的线程需要被唤醒
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
//本文不分析Condition,略过,以后会分析。
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
//略过
static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
// 取值为上面的1、-1、-2、-3
// 这么理解,暂时只需要知道如果这个值 大于0 代表此线程取消了等待
// 如ReentrantLock是可以指定timeout的
volatile int waitStatus;
/**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*/
//前驱节点
volatile Node prev;
/**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*/
//后继节点
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
//当前节点的值
volatile Thread thread;
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*/
//condition相关,略。
Node nextWaiter;
Node 的数据结构其实也挺简单的,就是 thread + waitStatus + pre + next 四个属性而已。
AQS重要方法
独占模式具体分析
acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire方法简要执行说明:
首先是tryAcquire方法会尝试的获取一下锁,成功的话就直接证明获取锁成功,acquire方法执行完毕(注意tryAcquire方法需要实现的子类根据自己的需要实现怎么抢锁,AQS不实现可以看到,只是抛出了异常,后面我们分析一个ReentrantLock的tryAcquire给大家感受下,暂时你只需要知道这个方法只是尝试获取锁)
tryAcquire方法
这里使用了模板方法的设计模式,tryAcquire方法留给子类实现,为什么不设置为抽象方法,这个是因为独占和公平对应两个不同的方法,如果设置为抽象方法子类就需要为两个方法分别写方法,十分不友好。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
tryAcquire方法抢锁失败就执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
先是addWaiter(Node.EXCLUSIVE)方法,这个方法表示将当前抢锁线程包装成结点并加入等待队列,返回包装后的结点
注意acquireQueued返回true表示线程发生中断,这时就会执行selfInterrupt方法响应中断。
由于tryAcquire没有具体实现,下面我们就接着看下addWaiter这个方法
addWaiter
private Node addWaiter(Node mode) {
//首先创建一个新节点,没什么好说的
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//把尾节点作为前驱节点
Node pred = tail;
//CAS方式入队列
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
//入队成功直接返回当前节点
return node;
}
}
//入队失败表示发生了竞争,自旋的方式插入新节点。
enq(node);
return node;
}
enq方法
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
和addWaiter方法类似,只是加了一个死循环,就不再赘述。
acquireQueued方法
addWaiter执行完毕之后就证明结点已经成功入队,所以就要开始执行acquireQueued方法进行资源的获取。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取结点的前驱结点
final Node p = node.predecessor();
/**
如果结点的前驱结点是head表示当前结点就是等待队列的第一个,
因为head结点并不指向一个实际的线程,所以这个时候就会执行下
tryAcquire函数尝试性的获取下锁。因为这个时候很有可能竞争成功
**/
if (p == head && tryAcquire(arg)) {
/**
拿到锁之后就更新头结点为当前结点(这个结点的线程已经拿到锁了,
所以更新为头结点也不会继续参与锁竞争,再次提示头结点不会参加竞争)
**/
setHead(node);
// 设置以前的头结点不指向其他结点,帮助gc
p.next = null; // help GC
failed = false;
return interrupted;
}
/**
上面前驱不是头结点或者获取锁失败就会执行shouldParkAfterFailedAcquire
函数判断是否应该挂起线程,注意只是判断并不会执行挂起线程的操作,挂起线程的
操作由后面的parkAndCheckInterrupt函数执行
**/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取前驱结点的waitStatus,这个决定着是否要对后续结点进行挂起操作
int ws = pred.waitStatus;
/**
如果ws的waitStatus=-1时,证明他的后继结点已经被park阻塞了后面到了竞争的时候会unpark唤醒后继结
点,所以如果结点的前驱结点waitStatus是-1,shouldParkAfterFailedAcquire就会判断需要park当前线程
所以返回true。
**/
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//ws>0证明前驱结点已经被取消所以需要往前找到没有被取消的结点ws>0
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
/**
前驱结点生成时,ws=0,所以如果是第一次执行shouldParkAfterFailedAcquire函数就会发现前驱结点
的ws = 0就会因为需要阻塞后面的结点设置为-1。
**/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
/**
注意把前驱结点ws设置为-1之后会虽然返回false,不挂起当前线程,注意只是这一次循环不挂起,因为
acquireQueued函数是一个死循环,所以到下一个循环如果前驱结点不是head并且tryAcquire竞争锁失败还
是会执行shouldParkAfterFailedAcquire方法,这个时候前驱结点已经为-1,所以就会直接返回true
**/
return false;
}
判断当前节点是否需要阻塞,如前驱节点的waitstatus=-1表示需要阻塞,如果前驱节点waitstatus>0表示前驱节点已作废,继续向前寻找,如果前驱节点waitstatus=0表示第一次执行此操作,需要设置前驱节点的waitstatus=-1,以待下一次循环时阻塞当前线程。
parkAndCheckInterrupt方法
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
LockSupport.park(this);
//线程被unpark唤醒时才会执行此操作
//返回线程的中断状态
return Thread.interrupted();
}
简洁来说该方法就是阻塞当前线程,当该线程被唤醒时返回线程的中断状态。
cancelAcquire方法
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
Node pred = node.prev;
// 找到没被取消的前驱节点同时将被取消节点移出。
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
//因为要取消当前结点所以修改当前结点得ws为CANCELLED
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
//如果不是尾结点
/**
满足下面三个条件,将pred的next指向node的下一节点
1.pred不是head节点:如果pred为头节点,
而node又被cancel,则node.next为等待队列中的第 一个节点,需要unpark唤醒
2.pred节点状态为SIGNAL或能更新为SIGNAL
3.pred的thread变量不能为null
**/
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//如果pred为头节点,则唤醒node的后节点,注意unparkSuccessor方法为唤醒当前结点得下一个结点
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
cancleAcquire函数主要是取消当前结点,将当前结点从等待队列中移出
同时遍历前面的结点将被取消的结点从队列中清除出去
unparkSuccessor 方法
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
//如果下一个结点为null或者ws为取消状态就未开始遍历找到正常状态的结点
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.unpark()方法唤醒阻塞的线程,注意被阻塞的线程从哪开始继续执行
LockSupport.unpark(s.thread);
release方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
简单回顾下整个过程
首先会通过tryAcquire方法尝试获取资源。
获取资源失败会通过addWaiter方法来线程包装成Node结点自旋的方式加入到等待队列
加入到等待队列之后就会该结点就会运行acquireQueued方法开始同等待队列的其他结点一起获取锁
先是判断该结点是不是第一个实际的等待的结点(不是head结点,head结点是空节点),如果是就用tryAcquire方法尝试获取锁,成功就更新head结点。
如果上面的操作失败,就会判断该线程是否需要被挂起(当前驱结点的ws为signal就会被挂起),然后就挂起该线程,当被唤醒之后就继续重复上面的步骤获取锁
当获取到锁以后就会有一个释放锁,释放锁的方法主要是release方法
同样tryRelease也交由子类去实现。
释放成功以后如果发现等待队列还有在等待获取锁的Node就用unparkSuccessor唤醒头结点的下一个结点。
共享模式具体分析
首先是acquireShared方法(对应独占模式得acquire方法)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared方法留给子类去实现,不多解释了。
doAcquireShared方法
// 如果tryAcquireShared方法返回结果小于0表示没有资源获取,需要将当前线程加入到队列。
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//如果当前结点是除了head得队列中的第一个结点那么就尝试获取资源
int r = tryAcquireShared(arg);
//tryAcquireShared返回值>=0资源获取成功,就开始进行更新结点操作
if (r >= 0) {
//这里注意下独占模式调用的是setHead方法,但是共享模式调用的是setHeadAndPropagate方法
setHeadAndPropagate(node, r);
p.next = null; // help GC
//这里注意下,这里响应中断,跟独占模式不同
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 获取资源失败就开始继续判断是否需要挂起线程,与独占模式得相同
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 出现异常就取消结点
if (failed)
cancelAcquire(node);
}
}
可以看到共享模式下的锁竞争同非共享模式下的步骤大体上相同
tryAcquireShared不同的是他会返回三种状态,负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。因为前面说过共享模式下资源是有很多的(或者说是有多个锁),允许最多由对应数量的线程持有相应的资源,一个线程持有一个资源量就-1直到0。
还有就是共享模式下的setHeadAndPropagate方法,下面一起来看下
setHeadAndPropagate方法
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
//首先更新head结点
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
/**
注意propagate表示的上次执行tryAcquireShared方法后的返回值。>0表示还有剩余资源,既然有剩余资源就继续唤醒后面等待获取资源的并且是共享模式得Node或者h == null或者当前获取到资源的得结点<0,signal需要唤醒后续结点
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
//唤醒后续共享模式得Node结点
doReleaseShared();
}
}
setHeadAndPropagate方法主要干了两件事
1、更新头结点
2、检查是否需要唤醒后续结点,满足上面说的三个条件
doReleaseShared方法
private void doReleaseShared() {
for (;;) {
Node h = head;
//如果头结点不为空,并且不是tail,队列还有结点
if (h != null && h != tail) {
int ws = h.waitStatus;
//如果head结点ws为signal就更新为0并唤醒后续结点
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
可以看到doReleaseShared方法主要就是唤醒阻塞队列中等待的结点。
从上面的分析可以知道,独占模式和共享模式的最大区别在于独占模式只允许一个线程持有资源,而共享模式下,当调用doAcquireShared时,会看后续的节点是否是共享模式,如果是,会通过unpark唤醒后续节点; 从前面的分析可以知道,被唤醒的节点是被堵塞在doAcquireShared的parkAndCheckInterrupt方法,因此唤醒之后,会再次调用setHeadAndPropagate,从而将等待共享锁的线程都唤醒,也就是说会将唤醒传播下去。
Reentrantlock
见名知意,可重入锁,我们一起来看看是怎么实现的。
Reentrantlock继承关系
public class ReentrantLock implements Lock, java.io.Serializable
类的内部类
ReentrantLock总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。
ReentrantLock类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面逐个进行分析。
Sync类
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
// 抽象方法,留给子类实现
abstract void lock();
/**
* 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) {
// 比较并设置状态成功,状态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;
}
// 释放资源
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;
}
// 判断资源是否被当前线程占有
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
// 新生一个条件
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
// 返回资源的占用线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 返回状态
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 资源是否被占用
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
Sync类存在如下方法和作用如下。
lock:锁定,留给子类实现。
nonfairTryAcquire:非公平模式下的尝试获取锁。
tryRelease:释放资源。
isHeldExclusively:判断资源是否被当前线程占有。
getOwner:返回占有资源的线程。
getHoldCount:返回状态。
isLocked:是否资源被占用。
NonfairSync类
NonfairSync类继承了Sync类,表示采用非公平策略获取锁,其实现了Sync类中抽象的lock方法。
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);
}
}
我们结合上述讲到过的AQS来理一理逻辑。
lock操作时,首先尝试获取一次锁。如果失败,则会调用AQS的acquire方法。
在acquire方法中,又会调用tryAcquire方法,最终调用Sync的nonfairTryAcquire。
nonfairTryAcquire方法中又会尝试一次获取锁,获取失败则会调用addWaiter和acquireQueued入队尾。
所以,非公平锁的实现就是在lock之后先尝试获取两次锁。都获取失败才会入队等待。
FairSync
FairSync类继承了Sync类,表示采用公平策略获取锁,其实现了Sync类中抽象的lock方法。
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();
// 如果资源状态为0
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;
}
}
还是结合AQS来理一理逻辑。
公平锁在调用lock时,直接会调用acquire方法。
然后acquire回调FairSync的tryAcquire,tryAcquire方法逻辑为如果队列为空即没有等待线程时或者当前线程持有锁时,则持有锁。否则返回false。继而执行addWaiter和acquireQueued入队尾。
总结
在掌握了AQS后,再来分析ReentrantLock的源码,就会非常简单,因为ReentrantLock的绝大部分操作都是基于AQS类的。所以,进行分析时要找准方向,就会事半功倍。谢谢各位同学观看~