await

调用sync.acquireSharedInterruptibly

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}

sync.acquireSharedInterruptibly
调用tryAcquireShared方法返回<0执行doAcquireSharedInterruptibly

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);
}

tryAcquireShared
尝试获取共享锁,获取成功返回1,否则-1

protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;
}

doAcquireSharedInterruptibly

private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.SHARED);boolean failed = true;try {for (;;) {final Node p = node.predecessor();//如果前一个node为队头,则通过tryAcquireShared尝试获取共享锁if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {//获取到锁执行setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())throw new InterruptedException();}} finally {//产生异常执行if (failed)cancelAcquire(node);}
}

addWaiter
调用addWaiter方法把队尾设置为当前node;如果队尾为空或者设置失败则调用enq方法

private Node addWaiter(Node mode) {Node node = new Node(Thread.currentThread(), mode);// Try the fast path of enq; backup to full enq on failureNode pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}enq(node);return node;
}

enq
调用enq方法队尾为空则创建空的队尾和队头,否则重新设置队尾为当前node,设置成功返回。enq和addWaiter方法不同在于enq循环执行一定会执行成功,不存在失败情况

private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // Must initializeif (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}
}

predecessor
调用predecessor方法获取前一个node

final Node predecessor() throws NullPointerException {Node p = prev;if (p == null)throw new NullPointerException();elsereturn p;
}static final int CANCELLED = 1; //取消
static final int SIGNAL = -1; //下个节点需要被唤醒
static final int CONDITION = -2; //线程在等待条件触发
static final int PROPAGATE = -3; //(共享锁)状态需要向后传播

shouldParkAfterFailedAcquire
获取当前node的前一个note的线程等待状态,如果为SIGNAL,那么返回true,大于0通过循环将当前节点之前所有取消状态的节点移出队列;其他状时,利用compareAndSetWaitStatus使前节点的状态为-1;如果是第一次await时ws状态是0,多次await时ws状态是0,最后肯定返回true

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;if (ws == Node.SIGNAL)return true;if (ws > 0) {do {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;
}

parkAndCheckInterrupt
调用park并返回线程是否已经中断

private final boolean parkAndCheckInterrupt() {LockSupport.park(this);return Thread.interrupted();
}

park
调用UNSAFE.park阻塞当前线程

public static void park(Object blocker) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, 0L);setBlocker(t, null);
}

setBlocker
在当前线程t的parkBlockerOffset位置设置blocker的引用

private static void setBlocker(Thread t, Object arg) {// Even though volatile, hotspot doesn't need a write barrier here.UNSAFE.putObject(t, parkBlockerOffset, arg);
}

UNSAFE.park

/*** 阻塞一个线程直到<a href="#unpark"><code>unpark</code></a>出现、线程* 被中断或者timeout时间到期。如果一个<code>unpark</code>调用已经出现了,* 这里只计数。timeout为0表示永不过期.当<code>isAbsolute</code>为true时,* timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时* 也可能不合理地返回(没有具体原因)* * @param isAbsolute true if the timeout is specified in milliseconds from*                   the epoch.*                   如果为true timeout的值是一个相对于新纪元之后的毫秒数* @param time either the number of nanoseconds to wait, or a time in*             milliseconds from the epoch to wait for.*             可以是一个要等待的纳秒数,或者是一个相对于新纪元之后的毫秒数直到*             到达这个时间点*/
UNSAFE.park(false, 0L);

countDown

调用sync.releaseShared

public void countDown() {sync.releaseShared(1);
}

releaseShared
执行tryReleaseShared成功后执行doReleaseShared

public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}

tryReleaseShared
更新state值为state-1,如果state新值为0返回true,否则false

protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}
}

doReleaseShared
只要等待队列有数据,获取队头等待状态,队头状态=-1其他node为等待时,则把队头等待状态置为初始,且调用unparkSuccessor方法;队头状态=0时,把队头状态置为-3传播到下一node

private void doReleaseShared() {/** Ensure that a release propagates, even if there are other* in-progress acquires/releases.  This proceeds in the usual* way of trying to unparkSuccessor of head if it needs* signal. But if it does not, status is set to PROPAGATE to* ensure that upon release, propagation continues.* Additionally, we must loop in case a new node is added* while we are doing this. Also, unlike other uses of* unparkSuccessor, we need to know if CAS to reset status* fails, if so rechecking.*/for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;if (ws == Node.SIGNAL) {if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;            // loop to recheck casesunparkSuccessor(h);}else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;                // loop on failed CAS}if (h == head)                   // loop if head changedbreak;}
}

unparkSuccessor
上面调用unparkSuccessor时,node的状态已经更改为0,且node.next存在,执行unpark方法

private void unparkSuccessor(Node node) {/** If status is negative (i.e., possibly needing signal) try* to clear in anticipation of signalling.  It is OK if this* fails or if status is changed by waiting thread.*/int ws = node.waitStatus;if (ws < 0)compareAndSetWaitStatus(node, ws, 0);/** Thread to unpark is held in successor, which is normally* just the next node.  But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*/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);
}

unpark
unpark执行完之后是如何更改head的?

public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);
}

UNSAFE.unpark

/*** Releases the block on a thread created by * <a href="#park"><code>park</code></a>.  This method can also be used* to terminate a blockage caused by a prior call to <code>park</code>.* This operation is unsafe, as the thread must be guaranteed to be* live.  This is true of Java, but not native code.* 释放被<a href="#park"><code>park</code></a>创建的在一个线程上的阻塞.这个* 方法也可以被使用来终止一个先前调用<code>park</code>导致的阻塞.* 这个操作操作时不安全的,因此线程必须保证是活的.这是java代码不是native代码。* @param thread the thread to unblock.*           要解除阻塞的线程*/
UNSAFE.unpark(thread);

CountDownLatch的await和countDown方法简单分析相关推荐

  1. java.util.ComparableTimSort中的sort()方法简单分析

    TimSort算法是一种起源于归并排序和插入排序的混合排序算法,设计初衷是为了在真实世界中的各种数据中能够有较好的性能. 该算法最初是由Tim Peters于2002年在Python语言中提出的. T ...

  2. CountDownLatch 的 .await() 的线程阻塞 和countDown() 计时唤醒

    1.CountDownLatch end = new CountDownLatch(N); //构造对象时候 需要传入参数N 2.end.await()  能够阻塞线程 直到调用N次end.count ...

  3. 业务数据分析中可能用到的简单的数据挖掘方法——相关性分析、主成分分析、因子分析

    业务数据分析中可能用到的简单的数据挖掘方法--相关性分析.主成分分析.因子分析 相关性分析 相关性分析通过相关系数来描述两个变量之间的相关性程度. 通过相关系数判断两者会不会相互影响,影响是正相关还是 ...

  4. JUC之CountDownLatch的源码和使用场景分析

    最近工作不饱和,写写文章充充电.何以解忧,唯有Coding.后续更新的文章涉及的方向有:ThreadPoolExecutor.Spring.MyBatis.ReentrantLock.CyclicBa ...

  5. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  6. 对tableView三种计算动态行高方法的分析

    tableView是一个神奇的东西,可以这么说,就算是一个初学者如果能把tableView玩的很6,那编一般的iOS的需求都问题不大了.tableView是日常开发中用烂了的控件,但是关于tableV ...

  7. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  8. ASIHTTPRequest源码简单分析

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比较底层的http库,功能比较少, ...

  9. python如何安装panda数据库_在Pycharm中安装Pandas库方法(简单易懂)

    开发环境的搭建是一件入门比较头疼的事情,在上期的文稿基础上,增加一项Anaconda的安装介绍.Anaconda是Python的一个发行版本,安装好了Anaconda就相当于安装好了Python,并且 ...

最新文章

  1. .NET C#生成随机颜色,可以控制亮度,生成暗色或者亮色 基于YUV模式判断颜色明亮度...
  2. 关于java中的各种流
  3. python探测端口_python检测远程服务器tcp端口的方法
  4. 腾讯offer是什么样子_月薪35K:2020腾讯Java后端开发详细面试流程
  5. 《计算机网络》第六章:传输层(The Transport Layer)
  6. python读取word element_Python:通过解析word将文本从docx提取到txt/文档.xm
  7. matlab数字图像处理实验
  8. TX2(linux系统、Ubuntu系统)输入法不显示拼音候选框、下拉框
  9. 华为C语言的编程规范
  10. SCI收录期刊——遥感学科
  11. 北京专精特新企业申报攻略
  12. MapReduce 编程实例:词频统计
  13. 饥荒联机版服务器控制台本地和在线,服务器,控制台饥荒服务器控制台命令,指令,常用命令,管理命令 - Welcome to XiongTianQi.CN...
  14. 打破认知:程序设计=算法+数据结构?
  15. 第一次开发EOS区块链的经验
  16. 线程和协程详解-python
  17. 黑魂复刻游戏的玩家输入模块——Unity随手记(2021.3.14)
  18. 中国IC设计Fabless排行榜 TOP100
  19. android.bp编译生成so,Android导入第三方静态库.a编译成动态库.so
  20. 企业级开发:Gitflow Workflow工作流

热门文章

  1. excel随机生成数字或者字母
  2. 题解报告——特别行动队
  3. MYSQL-行转列实例
  4. 电量优化 之 导出Bugreport日志
  5. 如何通过PC传输视频文件到IPAD中指定的APP
  6. 河南大学计算机院实训安排,关于做好 2019-2020学年本科生实习工作的通知
  7. 小能手英语口语学习笔记 1 48个国际音标
  8. Selenium2鼠标点击操作笔记
  9. Python学习笔记---day07数据类型(下)
  10. Optimism Rollup原理以及使用教程