2026/4/16 22:22:29
网站建设
项目流程
茂名网站开发,比翼网站建设,百度主页网址,免费网站搭建系统signal/signalAll 实现原理 调用 condition 的 signal 或者 signalAll 方法可以将等待队列中等待时间最长的节点移动到同步队列中#xff0c;使得该节点能够有机会获得 lock。等待队列是先进先出#xff08;FIFO#xff09;的#xff0c;所以等待队列的头节点必然会是等待时…signal/signalAll 实现原理调用 condition 的 signal 或者 signalAll 方法可以将等待队列中等待时间最长的节点移动到同步队列中使得该节点能够有机会获得 lock。等待队列是先进先出FIFO的所以等待队列的头节点必然会是等待时间最长的节点也就是每次调用 condition 的 signal 方法都会将头节点移动到同步队列中。signal 方法源码如下public final void signal() { //1. 先检测当前线程是否已经获取lock if (!isHeldExclusively()) throw new IllegalMonitorStateException(); //2. 获取等待队列中第一个节点之后的操作都是针对这个节点 Node first firstWaiter; if (first ! null) doSignal(first); }signal 方法首先会检测当前线程是否已经获取了 lock如果没有获取 lock 会直接抛出异常如果获取的话再得到等待队列的头节点之后的 doSignal 方法也是基于该节点。doSignal 方法源码如下private void doSignal(Node first) { do { if ( (firstWaiter first.nextWaiter) null) lastWaiter null; //1. 将头节点从等待队列中移除 first.nextWaiter null; //2. while中transferForSignal方法对头节点做真正的处理 } while (!transferForSignal(first) (first firstWaiter) ! null); }具体逻辑请看注释真正对头节点做处理的逻辑在transferForSignal方法中该方法源码为final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ //1. 更新状态为0 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; /* * Splice onto queue and try to set waitStatus of predecessor to * indicate that thread is (probably) waiting. If cancelled or * attempt to set waitStatus fails, wake up to resync (in which * case the waitStatus can be transiently and harmlessly wrong). */ //2.将该节点移入到同步队列中去 Node p enq(node); int ws p.waitStatus; if (ws 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }关键逻辑请看注释这段代码主要做了两件事情1.将头节点的状态更改为 CONDITION2.调用 enq 方法将该节点尾插入到同步队列中关于 enq 方法请看 AQS 的底层实现这篇文章。调用 condition.signal 方法的前提条件是当前线程已经获取了 lock该方法会使等待队列中的头节点即等待时间最长的那个节点移入到同步队列而移入到同步队列后才有机会被唤醒即从 await 方法中的LockSupport.park(this)方法中返回才有机会让调用 await 方法的线程成功退出。signal 执行示意图如下图sigllAll 与 sigal 方法的区别体现在 doSignalAll 方法上前面我们已经知道doSignal 方法只会对等待队列的头节点进行操作doSignalAll 的源码如下private void doSignalAll(Node first) { lastWaiter firstWaiter null; do { Node next first.nextWaiter; first.nextWaiter null; transferForSignal(first); first next; } while (first ! null); }该方法会将等待队列中的每一个节点都移入到同步队列中即“通知”当前调用condition.await()方法的每一个线程。await 与 signal/signalAllawait、signal 和 signalAll 方法就像一个开关控制着线程 A等待方和线程 B通知方。它们之间的关系可以用下面这幅图来说明会更贴切线程 awaitThread 先通过lock.lock()方法获取锁成功后调用 condition.await 方法进入等待队列而另一个线程 signalThread 通过lock.lock()方法获取锁成功后调用了 condition.signal 或者 signalAll 方法使得线程 awaitThread 能够有机会移入到同步队列中当其他线程释放 lock 后使得线程 awaitThread 能够有机会获取 lock从而使得线程 awaitThread 能够从 await 方法中退出并执行后续操作。如果 awaitThread 获取 lock 失败会直接进入到同步队列。