1,创建ReetrantLock对象
ReentrantLock lock = new ReentrantLock(true);
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2,加锁
lock.lock();
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
为了便于理解,想象一个场景,假如有三个线程分别为A,B,C,同时加锁,在此之前并没有其它线程加锁。
2.1 tryAcquire
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;
}
A,B,C三个线程同时开始执行tryAcquire方法,当三个线程同时调用getState()得到c的值都为0(根据之前的场景设定,没有其它线程加锁)。并且因为此时AQS队列中并没有等待获取锁的线程,所以!hasQueuedPredecessors()为true,因此会继续调用符号&&后面的方法compareAndSetState方法。
2.2 compareAndSetState
这个方法是把锁状态state从0设置为1,假如A先开始执行该方法,因为该方法是线程安全的,所以当A调用没有结束之前,B和C只能等待。
当A调用完之后,成功将state从0设置为1,表示加锁成功,并且把当前线程A设置拥有独占访问权的线程,返回true到上一层逻辑。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
此时!tryAcquire(arg)=false,因为是&&所以不会执行后面的acquireQueued(addWaiter(Node.EXCLUSIVE), arg),加锁成功,A线程不会被阻塞。
B和C此时开始调用compareAndSetState(0, acquires),因为它们的原始数据是0,但此时state已经为1,所以B和C都不会设置成功,返回false,!tryAcquire(arg)=true,因此会继续执行符号&&后面的方法acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
2.3 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;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
这个方法是将线程对应的结点Node放入队列中排队,因为此时队列为空,所以(pred != null)=false,会执行enq(node)方法。
2.4 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;
}
}
}
}
首先判断队列尾部是否为空,因为此时队列为空所以(tail==null)=true开始执行compareAndSetHead设置头结点(此方法相当于初始化队列,在队列中放入一个Node,这个Node不包含线程信息,将队列的头和尾都指向这个Node),初始化之后的数据结构如下所示:
因为compareAndSetHead方法是线程安全的,假如B先执行这个方法,C必须等待,B执行完compareAndSetHead之后,C就可以执行,因为B已经初始化完成,C初始化就会失败,失败之后,C继续循环执行。B初始化队列成功后也会再次循环,因为B已经初始化成功,所以此时tail不为空,假如B和C也是同时执行到相同逻辑,假设B对应Node为:NodeB,C对应Node为:NodeC,尾结点为tailNode,此时会同时将NodeB和NodeC的前一个结点同时设置为tailNode,这是不合理的,因为尾结点只能有一个,所以才有compareAndSetTail方法,通过线程安全方法保证只能有一个Node被成功设置为tailNode,如果B先执行compareAndSetTail,C必须等待,当B执行完之后,设置尾结点成功,结束循环。此时的数据结构如下所示:
C调用compareAndSetTail必定失败,因为此时tailNode已经改变,C会继续循环,重新获取最新的tailNode,然后调用compareAndSetTail方法,将NodeC设置为尾结点,此时队列中数据为:
2.5 acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
addWaiter执行完之后会将线程对应结点放入队列,并且返回结点Node
假如B和C同时执行到这个方法,首先先获取对应结点的前一个结点,B结点对应的前一个结点是headNode,执行符号&&后面的方法tryAcquire,再次尝试加锁,
此时分两种情况:
1,如果A已经释放锁,B会成功加锁,然后将NodeB设置为headNode,移除原headNode,此时的数据结构为:
此时B已经加锁成功,不会被阻塞,走加锁成功逻辑
2,如果A此时还没有释放锁,B加锁失败,接着往后执行shouldParkAfterFailedAcquire,因为NodeB为新节点,NodeB.preNode.waitStatus为0,如下所示:
执行compareAndSetWaitStatus方法后数据结构如下,headNode.waitStatus=-1
然后返回false,回到上一层逻辑,继续循环再次尝试加锁,
如果加锁成功参考上面说的第一种情况,
如果还是没加锁成功,此时shouldParkAfterFailedAcquire为true(刚刚执行compareAndSetWaitStatus将NodeB.preNode.waitStatus设置为Node.SIGNAL),
执行符号&&后面的parkAndCheckInterrupt方法阻塞当前线程。
如果C和B同时执行该方法,因为此时NodeC.preNode不是headNode,参考以上第二种情况。
如果C比B后执行该方法,这时候分两种情况:
1,A释放锁,B获取锁成功,此时NodeC.preNode=headNode,参考以上第一种情况
2,A没有释放锁,B获取锁失败,此时NodeC.preNode!=headNode,参考以上第二种情况
3,解锁
设定一个场景,假设现在B和C都在队列中排队
lock.unlock();
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
3.1 tryRelease
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;
}
这里为什么不是直接将state设置为0,这是因为考虑到重入锁的情况,比如A线程可以多次调用lock.lock()加锁,每调用一次,state就会在原来的基础上加1。如果想要释放锁,lock.unlock()方法调用次数必须和lock.lock()调用次数一致,所以这里用getState()-releases,而不是直接将state设置为0,假如A线程只加了一次锁,现在执行完了,要释放锁,getState得到的值为1,int c= (1-1)=0,满足条件(c==0)=true,设置free=true,设置当前独占线程为空,设置state为0,返回free=true,返回到上一层代码:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease为true,继续执行接下来的逻辑
取出当前headNode,再回顾一下现在的数据结构如下:
headNode不为空,并且headNode.waitStatus!=0满足条件,开始执行unparkSuccessor方法
3.2 unparkSuccessor
private void unparkSuccessor(Node node) {
/*
* 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;
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(s.thread);
}
参数node为headNode,headNode.next为NodeB,满足条件s!=null,唤醒线程B,回顾一下阻塞代码:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
当线程B被唤醒之后,进入下次循环,获取前一个结点为headNode,满足p==head,然后执行tryAcquire方法,准备尝试加锁,因为A已经释放锁,此时B会加锁成功,然后设置头结点为NodeB,把原headNode从队列中移除,此时的数据结构如下: