1.第一个线程进来
FairSync里的lock方法
final void lock() {
// 加锁成功后,修改的值
acquire(1);
}
其抽象父类的方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
判中的第一个tryAcquire方法,
其父类方法时这样的,但是我们要看的时fairSync(公平锁的)
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean tryAcquire(int acquires) {
// 拿到当前线程
final Thread current = Thread.currentThread();
// 拿到锁的状态;C = 0 就是没有占用锁
int c = getState();
// 没有人占用,
if (c == 0) {
// hasQueuedPredecessors 是否有线程等待
// 当hasQueuedPredecessors返false,compareAndSetState就加锁
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的属性
// 头节点
private transient volatile Node head;
// 尾节点
private transient volatile Node tail;
// 线程的状态(计数)
private volatile int state;
aqs的队列里,头节点header线程对象一直是null,所有等待线程都是排在后面的
public final boolean hasQueuedPredecessors() {
// 队尾
Node t = tail; // Read fields in reverse initialization order
// 队头
Node h = head;
Node s;
// 第一个线程来时,h!=t 返回的时false,队列还没初始化,两个都是null
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
再返回acquire方法
public final void acquire(int arg) {
// 如果第一个线程拿到锁,返回true,那么这里第一个判断就返回false,就结束执行
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
2.第二个线程来的时候,再次走tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 拿到锁的状态;C = 1 就是没有占用锁
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 当C不等于0 时,判断当前线程是否是是加锁的那个线程,如果这个判断进去就表示,重入锁
else if (current == getExclusiveOwnerThread()) {
// 这里acquire = 1,nextc = 2
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 这里设置state = 2,表示该线程进入2次,然后返回true
setState(nextc);
return true;
}
// 当前面两个判断走完,基本就确定该线程是拿不到锁的
return false;
}
}
返回后,进入acquireQueued方法
public final void acquire(int arg) {
// 如果第一个线程拿到锁,返回true,那么这里第一个判断就返回false,就结束执行
if (!tryAcquire(arg) &&
// 如果第二个线程在上一个操作中没有获取到锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
// 实例化当前线程节点
Node node = new Node(Thread.currentThread(), mode);
// 这里tail为空,因为还没初始化
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 进入这个方法;进行类似添加的操作,将node添加到队列中
enq(node);
return node;
}
只有一个线程来的时候,并没有走队列的逻辑,只是判断了队列是否由队头队尾,然后就设置了加锁状态了,所有当第二个线程来的时候,aqs队列还是没有初始化的。
private Node enq(final Node node) {
for (;;) {
// 将tail(null)给t
Node t = tail;
// 第一次,
if (t == null) { // Must initialize
// new 一个node给队头
if (compareAndSetHead(new Node()))
// 再把队头给tail(队尾)
tail = head;
} else {
// 再次循环进入,将t(现在已经有实例化后的了)设置为自己的前一个节点
node.prev = t;
// 将自己加到队尾,设置前一个节点的下一个节点为自己
if (compareAndSetTail(t, node)) {
t.next = node;
// 返回当前节点的上一个节点
return t;
}
}
}
}
这时,可以看到,第一次for,它new 了一个队头,空线程对象,并且tail = head,第二次for,t = tail = head
,compareAndSetTail设置当前节点为队尾,t.next 执行当前线程节点,return 跳出循环。
为什么头节点的线程对象要设置为空的??
第一个线程来的时候,拿到锁,当前线程对象已经有了,aqs里的队列里不需要再保存一次,而且,队列里的对拿到锁的线程对象不做任何操作,无意义。
然后结束返回:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
// addWaiter将当前节点加入到队尾
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
通过判断当前节点是不是在队头来判断是不是被park
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;
// 返回false
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这里的node是上一个节点,第二个线程进来,那么node就是头节点,它会判断是否是头节点,然后tryAcquire尝试加锁。
第一个判断中,加锁失败后,进入第二个判断,然后进入这个方法
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// Node.SIGNAL = -1,初始化后的这个状态是0,表示等待
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 进入这个方法,将上一个节点的watiStatus赋值为 -1,表示上一个节点时活跃状态,且下一个线程节点需要运行
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
waitStatus包含的状态有:
走完这个方法后,因为循环,又会走到shouldParkAfterFailedAcquire
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;
}
// 第一次进入后修改值后,再循环后又会进到这里,这时shouldParkAfterFailedAcquire返回为true,
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
第二次循环shouldParkAfterFailedAcquire方法返回true后,执行parkAndCheckInterrupt方法进行park,尝试获取锁,然后获取不到锁,如果获取不到就阻塞,返回中断标记并重置状态
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
如果park拿到锁了,就加锁成功,那么interrupted就是false,就不会进入selfInterrupt方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
然后上面的步骤再来一遍进入shouldParkAfterFailedAcquire
而非公平锁的lock,再加锁失败后就进入排队,就和上面的公平锁是一样的流程。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果加锁失败,就和公平锁的排队一样
acquire(1);
}
大概画了一个AQS结构:
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;
}
protected final boolean tryRelease(int releases) {
// 如果state = 1(线程状态非重入锁),c = 0
int c = getState() - releases;
// 这里判断当前线程是否是加锁的线程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 修改状态
free = true;
// 修改加锁的线程为null
setExclusiveOwnerThread(null);
}
// 修改状态为 c = 0
setState(c);
// 返回true
return free;
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
// 判断头节点是否为null,且该线程节点是否是活跃状态,如果只有一个线程时,没有等待队列,head = null,h = null,
// 如果时多于1个线程时,h != null,判断waitStatus状态,是否时队头,因为在添加等待队列的时候,会把上一个节点的waitStatus改为-1,那么waitStatus = 0 就是表示时队尾
if (h != null && h.waitStatus != 0)
// 释放
unparkSuccessor(h);
// 返回true
return true;
}
return false;
}
进入unparkSuccessor这个方法表示,有多个线程在排队,且该线程不在队尾,时队头waitStatus = -1
private void unparkSuccessor(Node node) {
// 头结点的状态
int ws = node.waitStatus;
if (ws < 0)
// 修改对头节点的waitStaus = 0
compareAndSetWaitStatus(node, ws, 0);
// 获取下一个节点
Node s = node.next;
// 下一个节点不可能为空,因为头节点为-1,是因为它的下一个节点修改的,waitStaus > 0 表示线程被取消,也不可能,所以不会进入
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);
}
假设:s == null || s.waitStatus > 0成立,那么这个对立的节点可能存在人为的操作致使队列发生变化,
if (s == null || s.waitStatus > 0) {
// 设置该节点为null
s = null;
// 条件从尾节点开始,只要不等于空,且不等于当传进来的节点也就是异常节点,就往前遍历
for (Node t = tail; t != null && t != node; t = t.prev)
// 找到等待状态 = -1的,也就是持有锁定节点
if (t.waitStatus <= 0)
s = t;
}
调用unsafe的unpark方法
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
这个方法执行后就会在执行park方法的地方继续执行,即第一个线程持有锁是,第二个线程也来排队,
private final boolean parkAndCheckInterrupt() {
// 在这里被阻塞了,
LockSupport.park(this);
return Thread.interrupted();
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
// 那么当调用unpark方法后,会从这里开始执行。
setBlocker(t, null);
}