AbstractQueuedSynchronizer 学习

本贴最后更新于 2002 天前,其中的信息可能已经时过境迁

AbstractQueuedSynchronizer

独占锁

ReentrantLock 实现
状态 数值 说明
CANCELLED 1 等待超时或者中断,需要从同步队列中取消
SIGNAL -1 后继节点处于等待状态,当前节点释放锁后将会唤醒后继节点
CONDITION -2 节点在等待队列中,节点线程等待在 Condition 上,其它线程对 Condition 调用 signal () 方法后,该节点将会从等待同步队列中移到同步队列中,然后等待获取锁。
PROPAGATE -3 表示下一次共享式同步状态获取将会无条件地传播下去
INITIAL 0 初始状态
独占锁获取的流程图

独占锁

public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } // 唤醒操作 private void unparkSuccessor(Node node) { int ws = node.waitStatus; // 将当前节点设置为0 if (ws < 0) compareAndSetWaitStatus(node, ws, 0); // 如果下一个节点状态大于0,表示已中断或取消,从队尾找到最后一个,也就是第一个可以唤醒的线程 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); }

Sync

// 获取锁 public final void acquire(int arg) { if (!tryAcquire(arg) && // 这里是将线程添加到阻塞队列里中,加入前还去尝试获取一次锁,否则就添加到队尾,等待唤醒 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

FairSync

static final class FairSync extends Sync { final void lock() { acquire(1); } 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; } }

NoFairSync

static final class NonfairSync extends Sync { final void lock() { // 和公平锁相比,这里会直接先进行一次CAS,成功就返回了 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1);// 这里去尝试获取锁 } //使用非公平锁的逻辑 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } // 非公平锁获取的逻辑,写在父类Sync中了 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 这里直接CAS 没有判断前面是否有节点 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } ...省略。 同上面公平锁的逻辑 } return false; } }

非公平锁和公平锁的区别

  1. 非公平锁在调用 tryAcquire 方法前,会直接尝试获取一次锁;
  2. tryAcquire 获取时,公平锁在尝试获取锁之前,还会判断是否有等待线程;
  3. 非公平锁在两次尝试获取锁失败后就会加入到等待队列,同公平锁一样,等待前一节点的唤醒;另外新节点加入到队列前,会把前面连续中断的线程移出队列。

解锁操作

public void unlock() { sync.release(1); }

共享锁

Semaphore 实现

共享锁

NoFair

public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // 尝试获取锁,获取锁失败,加入队列 if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } // 加入队列操作 private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); // 当前节点前一个节点是否获取锁的节点,尝试去再去获取一次锁 if (p == head) { int r = tryAcquireShared(arg); // 获取锁成功 if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } ...省略 } private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node); if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; if (s == null || s.isShared()) // 唤醒 doReleaseShared(); } } private void doReleaseShared() { for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; unparkSuccessor(h); // 唤醒下一个节点 } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }
共享锁获取逻辑
  1. acquireShared 也会首先尝试获取状态,如果获取成功,直接返回成功。
  2. 获取失败后,和 acquire 一样,会在链表中新增一个节点,两者不同之处在于,acquire 是新增一个 EXCLUSIVE (独占) 类型的节点,acquireSharedSHARED (共享 类型的节点,
  3. 新增完节点之后,程序就会检查自己的前一个节点是否为第一个节点,如果是,就再次调用 tryAcquireShared () 尝试请求状态。这一步和 acquire 是一样的,区别在于获取成功之后的操作。
  4. tryAcquireShared () 会有 3 种可能的返回值,负值:代表获取失败。0:获取成功但是没有剩于的状态了。正值:获取成功而且还有可用的状态。当 tryAcquireShared () >= 0 时, 代表状态获取成功。获取成功后,程序就会把当前节点设置为头节点,如果返回大于 0 并且下一个节点是共享模式, 程序就会唤醒下一个等待状态的线程。
  5. 获取失败的操作就和 acquire 是一样的了,就是调用就会调用 shouldParkAfterFailedAcquire () 阻塞自己并清理一个链表。
共享锁释放逻辑
  1. 整体逻辑和独占锁相似。
  2. releaseShared 的区别在于对 waitStatus 的处理,release 遇到 0 时会返回失败,而 shared 在遇到 0 时, 会把它设置为传播来保证级联唤醒节点时不会中断。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...