硬核干货:5W字17张高清图理解同步器框架AbstractQueuedSynchronizer (11)

其实上面的await()逻辑并不复杂,前提是理解了对象监视器锁那套等待和唤醒的机制(由JVM实现,C语言学得好的可以去看下源码),这里只是通过算法和数据结构重新进行了一次实现。await()主要使用了两个队列:同步等待队列和条件等待队列。我们先假设有两个线程thread-1和thread-2调用了下面的代码中的process()方法:

ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void process(){ try{ lock.lock(); condition.await(); // 省略其他逻辑... }finally{ lock.unlock(); } }

ReentrantLock使用的是AQS独占模式的实现,因此在调用lock()方法的时候,同步等待队列的一个瞬时快照(假设线程thread-1先加入同步等待队列)可能如下:

j-a-q-s-ex-14

接着,线程thread-1所在节点是头节点的后继节点,获取锁成功,它解除阻塞后可以调用await()方法,这个时候会释放同步等待队列中的所有等待节点,也就是线程thread-2所在的节点也被释放,因此线程thread-2也会调用await()方法:

j-a-q-s-ex-15

只要有线程能够到达await()方法,那么原来的同步器中的同步等待队列就会释放所有阻塞节点,表现为释放锁,然后这些释放掉的节点会加入到条件等待队列中,条件等待队列中的节点也是阻塞的,这个时候只有通过signal()或者signalAll()进行队列元素转移才有机会唤醒阻塞的线程。因此接着看signal()和signalAll()的源码实现:

// 从等待队列中移动一个等待时间最长的线程(如果过存在的话)到锁同步等待队列中 public final void signal() { // 判断当前线程是否和独占线程一致,其实就是此操作需要在锁代码块中执行 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first); } // 基于第一个等待节点进行Signal操作 private void doSignal(Node first) { do { // 首节点的下一个等待节点为空,说明只剩下一个等待节点 if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; // 当前处理节点从链表从移除 first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); } // 唤醒的转换操作 final boolean transferForSignal(Node node) { // CAS更新节点状态由CONDITION到0,更新失败则返回false不唤醒 if (!node.compareAndSetWaitStatus(Node.CONDITION, 0)) return false; // 节点作为新节点重新加入到同步等待队列 Node p = enq(node); int ws = p.waitStatus; // 取消或者更新节点等待状态为SIGNAL的节点需要解除阻塞进行重新同步,这里的操作只针对取消和状态异常的节点 if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; } // 从等待队列中移动所有等待时间最长的线程(如果过存在的话)到锁同步等待队列中 public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first); } // 基于第一个等待节点进行SignalAll操作 private void doSignalAll(Node first) { // 置空lastWaiter和firstWaiter lastWaiter = firstWaiter = null; do { // 获取下一个等待节点 Node next = first.nextWaiter; // 当前处理节点从链表从移除 first.nextWaiter = null; // 处理当前节点 transferForSignal(first); // 更新中间引用 first = next; } while (first != null); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywszj.html