CountDownLatch的原理学习

转载:http://blog.csdn.net/yanyan19880509/article/details/52349056

前言

前面介绍了ReentrantLock,又叫排他锁,本篇主要通过CountDownLatch的学习来了解Java并发包中是如何实现共享锁的。

CountDownLatch使用解说

CountDownLatch是java5中新增的一个并发工具类,其使用非常简单,下面通过伪代码简单看一下使用方式:

这是一个使用CountDownLatch非常简单的例子,创建的时候,需要指定一个初始状态值,本例为2,主线程调用 latch.await时,除非latch状态值为0,否则会一直阻塞休眠。当所有任务执行完后,主线程唤醒,最终执行打印动作。

以上只是一个最简单的例子,接着咱们再来看一个,这回,咱们想要在任务执行完后做更多的事情,如下图所示:

这一次,在线程3和线程4中,分别调用了latch.await(),当latch状态值为0时,这两个线程将会继续执行任务,但是顺序性是无法保证的。

CountDownLatch的方便之处在于,你可以在一个线程中使用,也可以在多个线程上使用,一切只依据状态值,这样便不会受限于任何的场景。

java共享锁模型

在java5提供的并发包下,有一个AbstractQueuedSynchronizer抽象类,也叫AQS,此类根据大部分并发共性作了一些抽象,便于开发者实现如排他锁,共享锁,条件等待等更高级的业务功能。它通过使用CAS和队列模型,出色的完成了抽象任务,在此向Doug Lea致敬。

AQS比较抽象,并且是优化精简的代码,如果一头扎进去,可能会比较容易迷失。本篇只解说CountDownLatch中使用到的共享锁模型。

我们以CountDownLatch第二个例子作为案例来分析一下,一开始,我们创建了一个CountDownLatch实例,

此时,AQS中,状态值state=2,对于 CountDownLatch 来说,state=2表示所有调用await方法的线程都应该阻塞,等到同一个latch被调用两次countDown后才能唤醒沉睡的线程。接着线程3和线程4执行了 await方法,这会的状态图如下:

注意,上面的通知状态是节点的属性,表示该节点出队后,必须唤醒其后续的节点线程。当线程1和线程2分别执行完latch.countDown方法后,会把state值置为0,此时,通过CAS成功置为0的那个线程将会同时承担起唤醒队列中第一个节点线程的任务,从上图可以看出,第一个节点即为线程3,当线程3恢复执行之后,其发现状态值为通知状态,所以会唤醒后续节点,即线程4节点,然后线程3继续做自己的事情,到这里,线程3和线程4都已经被唤醒,CountDownLatch功成身退。

上面的流程,如果落实到代码,把 state置为0的那个线程,会判断head指向节点的状态,如果为通知状态,则唤醒后续节点,即线程3节点,然后head指向线程3节点,head指向的旧节点会被删除掉。当线程3恢复执行后,发现自身为通知状态,又会把head指向线程4节点,然后删除自身节点,并唤醒 
线程4。

这里可能读者会有个疑问,线程节点的状态是什么时候设置上去的。其实,一个线程在阻塞之前,就会把它前面的节点设置为通知状态,这样便可以实现链式唤醒机制了。

时间: 2024-11-08 19:58:41

CountDownLatch的原理学习的相关文章

Android自复制传播APP原理学习(翻译)

 Android自复制传播APP原理学习(翻译) 1 背景介绍 论文链接:http://arxiv.org/abs/1511.00444 项目地址:https://github.com/Tribler/self-compile-Android 吃完晚饭偶然看到这篇论文,当时就被吸引了,马上翻译总结了一下.如有错误欢迎斧正. 该论文的研究出发点比较高大上这里我们就不多说了,简而言之就是想通过移动设备来实现一个自组网,在发生灾难的时候,手机之间能够自动传输关键数据,减少损失.整个目标通过设计一个能够

编译原理学习导论

编译原理学习导论 大学课程为什么要开设编译原理呢?这门课程关注的是编译器方面的产生原理和技术问题,似乎和计算机的基础领域不沾边,但是编译原理却一直作为大学本科的必修课程,同一时候也成为了研究生入学考试的必考内容.编译原理及技术从本质上来讲就是一个算法问题而已,当然因为这个问题十分复杂,其解决算法也相对复杂.我们学的数据结构与算法分析也是讲算法的,只是讲的基础算法,换句话说讲的是算法导论,而编译原理这门课程讲的就是比較专注解决一种的算法了.在20世纪50年代,编译器的编写一直被觉得是十分困难的事情

编译原理学习笔记 -- 绪论1

1. 语言处理器 语言处理系统 _________ 经过预 _______ 源程序 --> |预处理器| --> 处理的 --> |编译器| --> 目标汇编程序 -------- 源程序 ------- _______ 可重定位的 ______________ --> |汇编器| --> 机器代码 --> |链接器/加载器| --> 目标机器代码 ------- -------------- ↑ 库文件/可重定位对象文件 预处理器:把源程序聚合在一起,并宏

计算机原理学习(7)-- x86-32 CPU和内存管理之分段管理

前言 前一篇我们介绍了内存管理中的分页试内存管理,分页的主要作用就是使得每个进程有一个独立的,完整的内存空间,通过虚拟内存技术,使得程序可以在较小的内存上运行,而进程之间内存空间相互独立,提高了安全性.这一篇将主要介绍内存管理中分段管理,以及两种的结合,也是目前计算机普遍采用的段页式内存管理.这也直接决定了的后面程序的编译,加载以及允许时的内存布局. 1. 内存分段 1.1 为什么分段? 在x86-16体系中,为了解决16位寄存器对20位地址线的寻址问题,引入了分段式内存管理.而CPU则使用CS

编译原理学习

编译原理学习笔记---- 不确定有穷自动机(NFA) 一个不确定的有穷自动机T是一个五元组,M={K,∑,f,S,Z} ⒈K是一个有穷集他的每一个元素称作一个状态. ⒉∑是一个字母表,他的每一个元素称为一个输入符号. ⒊f是一个从Kx∑*到K的子集映射即K*∑*->2^K,其中2^K表示K的幂集. ⒋S包含于K集,是一个非空初态集合. ⒌Z包含于K是一个非空的终态集合. 确定有穷自动机(DFA) 一个确定的有穷自动机M是一个五元组:M=(K, ∑,f,S,Z)其中, 1)K是一个有穷集,他的每个

CountDownLatch实现原理

CountDownLatch的原理 这个类一般的应用场景为:一个线程等待另外N(N>=1)个线程的事情搞完了,自己再搞事情.具体应用代码大致如下: public class CountDownLatchTest { private static final int THREAD_COUNT = 10; private CountDownLatch latch = new CountDownLatch(THREAD_COUNT); public void test() throws Excepti

Java锁原理学习

Java锁原理学习 为了学习Java锁的原理,参照ReentrantLock实现了自己的可重入锁,代码如下: 先上AQS的相关方法: // AQS = AbstractQueuedSynchronizer, 抽象队列同步器 // 它提供了对资源的占用.释放,线程的等待.唤醒等接口和具体实现 // 它维护了一个volatile int state来代表共享资源的状态,和一个FIFO线程等待队列 // 获取排它锁 // 先尝试获取锁,如果获取不到则添加到等待队列 // 等待队列通过 LockSupp

Tomcat内部结构及工作原理学习

Tomcat原本是Servlet/JSP的一个调试工具,后来才发展为一个Servlet/JSP的容器. Tomcat作为Servlet容器,负责处理客户请求,把请求传送给Servlet并把结果返回给客户. Servlet容器与Servlet接口是由Java Servlet API定义的. Tomcat的四种组件 每个元素都代表了一个种Tomcat组件,它们可以分成四类: 第一种:顶层类元素 比如Server和Service 第二种:连接器类元素 连接器类元素代表了介于客户与服务之间的通信接口,负

lucene原理学习笔记(一)

最近在学习lucene原理方面的知识,将学习中学到的知识和问题记录下来,今天学习的主要内容就是关于索引方面的内容.我们知道lucene是实现全文检索的工具包,要在工程里面加入搜索的功能还需要基于lucene的api进行开发.那么全文检索的步骤分为哪几步呢.全文检索大体上分为两个步骤,索引的创建和搜索索引.于是乎全文检索就涉及到如下几个问题: 1.如何创建索引.2.如果搜索索引.3.索引里面存储的信息都有什么. 索引里面存储的都有什么信息呢,可以用一下的例子来说明,一个文件包含多个字符,当要查询一