原理

1.非公平锁加锁流程(失败 park 并添加到阻塞链表)
public void lock() { sync.lock(); }
lock()
// 非公平锁 static final class NonfairSync extends Sync { /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) // 加锁成功,设置锁 owner 为当前线程就结束了。直接返回 setExclusiveOwnerThread(Thread.currentThread()); // 加锁失败 else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
acquire
会再试着加锁一次,如果再失败,就会进入等待队列
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
addWaiter
cas 获取锁(!tryAcquire(arg))失败后:addWaiter 将等待的线程加入链表中
参数:Node.EXCLUSIVE 表示当前是独占模式,即只有一个线程能够访问资源
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { // 当前节点的 prev 指向之前的尾部节点 pred node.prev = pred; // CAS 设置链表 tail 变量为当前节点 node,期待前值为【pred】 if (compareAndSetTail(pred, node)) { // 之前的尾部节点 pred 的 next 指向当前节点 pred.next = node; return node; } } // pred==null 也就是链表没初始化 // 或者 CAS 设置 tail 为当前节点失败时 进入 enq(node); return node; }
enq
private Node enq(final Node node) { // 循环进行尝试 for (;;) { // 每次循环获取最新的尾部节点 Node t = tail; // 尾部节点为空,即链表还没初始化的情况 if (t == null) { // Must initialize // CAS 将链表 head 变量设为当前节点,期待前值是 null if (compareAndSetHead(new Node())) // 新链表,tail 也为当前节点 tail = head; } // addWaiter CAS 将链表的 tail 变量设为当前节点 失败时(也就是说 tail 被别的线程改掉了),会第一次走到这里,重新尝试 else { // 当前节点 prev 指向最新的尾部节点 t node.prev = t; // CAS 将链表的 tail 变量设为当前节点,期待前值为 t if (compareAndSetTail(t, node)) { // 之前的尾部节点 t 的 next 指向当前节点 t.next = node;
// 一定是添加到队列成功了,才会退出循环返回 return t; } } } }
acquireQueued
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 当前节点的前驱节点 final Node p = node.predecessor(); // 如果当前节点前一个节点是头节点,说明当前节点是等待队列里的第一个(头节点是虚的)
// 此时再尝试获取一次锁 tryAcquire if (p == head && tryAcquire(arg)) { // 如果获取成功了。把当前节点设为虚头节点(令 head=当前节点,并且 thread prev 置为 null) setHead(node); // 之前的头节点 p.next = null; // help GC // 失败标志位 failed = false; // 返回中断标志位 return interrupted; } // 检查当前节点是否需要阻塞 if (shouldParkAfterFailedAcquire(p, node) && // 在这里 park 等待。 parkAndCheckInterrupt()) // interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
parkAndCheckInterrupt
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
shouldParkAfterFailedAcquire
注意不要混淆两个 state
state 是一个 AQS 实例中的一个属性,可以用 0 没被锁,1 加了锁
在等待队列中的节点都是没获取到锁的线程,Node 有一个属性叫 waitStaus,0 是初始状态
/** waitStatus value to indicate thread has cancelled */ static final int CANCELLED = 1; /** waitStatus value to indicate successor's thread needs unparking */ static final int SIGNAL = -1; /** waitStatus value to indicate thread is waiting on condition */ static final int CONDITION = -2; /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate */ static final int PROPAGATE = -3;
检查是否需要阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { // 获取前一个节点的状态 int ws = pred.waitStatus; // 如果前一个节点在阻塞,所以这个也阻塞 park if (ws == Node.SIGNAL) return true;
// >0 表示取消状态(>0的只有一个就是0取消) if (ws > 0) { // 上一个节点取消, 那么重构删除前面所有取消的节点, 返回到外层循环重试 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; }
// 前驱节点的 waitStatus 是【初始默认状态 0】,那么将前驱节点改为 -1。
// -1 表示它有责任唤醒它的后继节点,因为要 park,所以必须保证可以有人来唤醒它,这个来唤醒它的就是前驱节点
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. */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
可以看到失败后,还会进行好几次获取锁的尝试,在第四次失败后,才会真正 Park
每个等待线程由它的前驱节点线程唤醒,所以前驱节点的 waitStatus 都是 -1

2.非公平锁解锁流程()