lock()函数
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
在lock函数中,如果某一个线程通过CAS操作,实现了state从0变为1。表示成功加锁了,因此把持有锁的线程设置为当前线程,即exclusiveOwnerThread这个属性。
如果没有成功
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
当获取锁失败时就会进入acquire(1)方法,在这个方法内会再次尝试获取锁,进入tryAcquire(1)方法再次尝试获取锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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函数中成功上锁一样的逻辑,如果发现持有锁的线程和当前线程一致,其实也是上锁成功,就是把status加一,表示锁重入。如果失败,就会进入acquireQueued方法,执行之前呢会执行addWaiter(Node.EXCLUSIVE)方法
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;
}
在addWaiter方法中,先把获取锁失败的线程打包成了一个Node对象(假设这是第一个阻塞的线程)因此AQS的尾指针就是null,pred是null进入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;
}
}
}
}
下图给了一个操作过程图
很简单,这个enq方法就是把阻塞的线程加入到一个对列里面。最后,这个addWaiter最终的作用是把阻塞的线程封装到Node对象里面,然后放到一个对列里面,并最后返回这个封装的Node对象。注意:在封装成Node的时候,waitStatus=0,相当于是Initalize状态
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);
}
}
在上面的源码中可以看到,只有当这个阻塞的节点的前驱节点是头节点的时候,才会继续尝试获取锁。如果获取锁成功了,那么就会把当前的这个Node节点设置为头节点,并把对以前节点的引用移除方便GC。注意:这些线程到现在都没有进行阻塞动作。同时,只有当该节点的前驱节点,这个线程才会有机会再次尝试获得锁。
如果当前的这个没有获取到锁的线程封装的Node对象的前驱节点不是头节点的时候,就会依次执行 shouldParkAfterFailedAcquire方法和parkAndCheckInterrupt方法。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
为了方便说明,明确一下,在ReentrantLock锁 的lock()方法中,waitStatus仅仅只有0和SIGNAL这两个状态。
同时传入的两个参数分别是一个Node对象和他的前驱Node节点对象。我们发现其实在这个方法中,主要是把前驱节点的waitStatus设置为SIGNAL。因为如果这个函数返回为false,那就会重新进入acquireQueued这个方法的for循环,重新执行,并且这次执行一定是true。因此进入下一个方法parkAndCheckInterrupt。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
从上面的函数看到,在这个方法中开始阻塞当前的线程。这才是真正使得线程进入阻塞状态的方法。