小白也能轻松操作:cv_unet_image-colorization图像上色全流程解析

核心内容摘要

IDEA + Groovy 脚本一键生成实体类:用法、原理与进阶实战
开源授权管理工具:解决Beyond Compare软件授权痛点的技术方案

告别官方限制:华为设备解锁工具PotatoNV全攻略

Condition 的 await 方法当调用condition.await()方法后会使当前获取锁的线程进入到等待队列如果该线程能够从await()方法返回的话一定是该线程获取了与 Condition 相关联的锁。

前面讲过了Condition 只是一个接口它的实现类为 ConditionObject是 AQS 的子类。

ConditionObject 的 await 方法源码如下public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //

将当前线程包装成Node尾插入到等待队列中 Node node addConditionWaiter(); //

释放当前线程所占用的lock在释放的过程中会唤醒同步队列中的下一个节点 int savedState fullyRelease(node); int interruptMode 0; while (!isOnSyncQueue(node)) { //

当前线程进入到等待状态 LockSupport.park(this); if ((interruptMode checkInterruptWhileWaiting(node)) !

break; } //

自旋等待获取到同步状态即获取到lock if (acquireQueued(node, savedState) interruptMode ! THROW_IE) interruptMode REINTERRUPT; if (node.nextWaiter ! null) // clean up if cancelled unlinkCancelledWaiters(); //

处理被中断的情况 if (interruptMode !

reportInterruptAfterWait(interruptMode); }代码的主要逻辑请看注释。

当前线程调用condition.await()方法后会释放 lock 然后加入到等待队列直到被signal/signalAll方法唤醒。

怎样将当前线程添加到等待队列调用 addConditionWaiter 方法会将当前线程添加到等待队列中源码如下private Node addConditionWaiter() { Node t lastWaiter; if (t ! null t.waitStatus ! Node.CONDITION) { //将不处于等待状态的节点从等待队列中移除 unlinkCancelledWaiters(); t lastWaiter; } Node node new Node(Thread.currentThread(), Node.CONDITION); //尾节点为空 if (t null) //将首节点指向node firstWaiter node; else //将尾节点的nextWaiter指向node节点 t.nextWaiter node; //尾节点指向node lastWaiter node; return node; }首先将 t 指向尾节点如果尾节点不为空并且它的waitStatus!-

为 CONDITION表示正在等待 Condition 条件则将不处于等待状态的节点从等待队列中移除并且将 t 指向新的尾节点。

然后将当前线程封装成 waitStatus 为-2 的节点追加到等待队列尾部。

如果尾节点为空则表明队列为空将首尾节点都指向当前节点。

如果尾节点不为空表明队列中有其他节点则将当前尾节点的 nextWaiter 指向当前节点将当前节点置为尾节点。

简单

总结一下这段代码的作用就是通过尾插入的方式将当前线程封装的 Node 插入到等待队列中同时可以看出Condtion 的等待队列是一个不带头节点的链式队列不带头节点是指在链表数据结构中链表的第一个节点就是实际存储的第一个数据元素而不是一个特定的头节点该节点不包含实际的数据。

1不带头节点的链表链表的第一个节点就是第一个实际的数据节点。

当链表为空时头引用通常称为 head指向 null。

2带头节点的链表链表有一个特殊的节点作为链表的开头这个特殊的节点称为头节点。

头节点通常不存储任何实际数据或者它的数据字段不被使用。

无论链表是否为空头节点总是存在的。

当链表为空时头节点的下一个节点指向 null。

使用头节点可以简化某些链表操作因为不必特殊处理第一个元素的插入和删除。

1不带头节点的链表public class Node { public int data; public Node next; public Node(int data) { this.data data; this.next null; } } public class LinkedListWithoutHead { public Node head; public void insert(int value) { Node newNode new Node(value); if (head null) { head newNode; } else { Node temp head; while (temp.next ! null) { temp temp.next; } temp.next newNode; } } }2带头节点的链表public class NodeWithHead { public int data; public NodeWithHead next; public NodeWithHead(int data) { this.data data; this.next null; } } public class LinkedListWithHead { private NodeWithHead head; public LinkedListWithHead() { head new NodeWithHead(-

; // 初始化头节点 } public void insert(int value) { NodeWithHead newNode new NodeWithHead(value); NodeWithHead temp head; while (temp.next ! null) { temp temp.next; } temp.next newNode; } }释放锁的过程将当前节点插入到等待对列之后会使当前线程释放 lock由 fullyRelease 方法实现源码如下final int fullyRelease(Node node) { //释放锁失败为true释放锁成功为false boolean failed true; try { //获取当前锁的state int savedState getState(); //释放锁成功的话 if (release(savedState)) { failed false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { if (failed) //释放锁失败的话将节点状态置为取消 node.waitStatus Node.CANCELLED; } }这段代码也很容易理解调用 AQS 的模板方法 release 释放 AQS 的同步状态并且唤醒在同步队列中头节点的后继节点引用的线程如果释放成功则正常返回若失败的话就抛出异常。

怎么从await方法中退出现在回过头再来看 await 方法其中有这样一段逻辑while (!isOnSyncQueue(node)) { //

当前线程进入到等待状态 LockSupport.park(this); if ((interruptMode checkInterruptWhileWaiting(node)) !

break; }isOnSyncQueue 方法用于判断当前线程所在的 Node 是否在同步队列中。

如果当前节点的 waitStatus-2说明它在等待队列中返回 false如果当前节点有前驱节点则证明它在 AQS 队列中但是前驱节点为空说明它是头节点而头节点是不参与锁竞争的也返回 false。

如果当前节点既不在等待队列中又不是 AQS 中的头节点且存在 next 节点说明它存在于 AQS 中直接返回 true。

看一下同步队列与等待队列的关系图当线程第一次调用 condition.await 方法时会进入到这个 while 循环然后通过LockSupport.park(this)使当前线程进入等待状态那么要想退出 await第一个前提条件就是要先退出这个 while 循环出口就只两个地方走到 break 退出 while 循环while 循环中的逻辑判断为 false。

出现第 1 种情况的条件是当前等待的线程被中断后代码会走到 break 退出第 2 种情况是当前节点被移动到了同步队列中即另外一个线程调用了 condition 的 signal 或者 signalAll 方法while 中逻辑判断为 false 后结束 while 循环。

总结一下退出 await 方法的前提条件是当前线程被中断或者调用 condition.signal 或者 condition.signalAll 使当前节点移动到同步队列后。

当退出 while 循环后会调用acquireQueued(node, savedState)该方法的作用是在自旋过程中线程不断尝试获取同步状态直到成功线程获取到 lock。

这样也说明了退出 await 方法必须是已经获得了 condition 引用关联的 lock。

await 方法示意图如下如图调用 condition.await 方法的线程必须是已经获得了 lock 的线程也就是当前线程是同步队列中的头节点。

调用该方法后会使得当前线程所封装的 Node 尾插入到等待队列中。

超时机制的支持condition 还额外支持超时机制使用者可调用 awaitNanos、awaitUtil 这两个方法

实现原理基本上与 AQS 中的 tryAcquire 方法如出一辙。

不响应中断的支持要想不响应中断可以调用condition.awaitUninterruptibly()方法该方法的源码如下public final void awaitUninterruptibly() { Node node addConditionWaiter(); int savedState fullyRelease(node); boolean interrupted false; while (!isOnSyncQueue(node)) { LockSupport.park(this); if (Thread.interrupted()) interrupted true; } if (acquireQueued(node, savedState) || interrupted) selfInterrupt(); }这段方法与上面的 await 方法基本一致只不过减少了对中断的处理。

freexxxⅹvidos1822-freexxxⅹvidos1822最新版N.20.71.80-2285安卓网应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123