三个好用的并发工具类

之前的文章中,咱们介绍了太多的底层原理技术以及新概念,本篇咱们轻松点,了解下 Java 并发包下、基于这些底层原理的三个框架工具类。java

它们分别是:git

  • 信号量 Semaphore
  • 倒计时门栓 CountDownLatch
  • 屏障 CyclicBarrier

因此,既然是工具类,那么必然是离不开特定的场景的,因而相互之间没有谁优谁劣,只有谁更合适。程序员

信号量 Semaphore

Semaphore 适用于什么样的使用场景呢,咱们举个通俗的例子:github

假如如今有一个停车场,里面有只十个停车位,当着十个停车位都被占用了,外面的车就不容许进入了,就必须在外面等着。出来一辆车才容许进去一辆车数据库

这个场景不一样于咱们通常的并发场景,通常来讲,咱们的临界资源只能容许一个线程进行访问,其余线程都地等着。微信

可是,有一种场景是,临界资源容许多个线程同时访问,超过限定数量的外的线程得阻塞等待。并发

这种情境使用原始的那一套也是能实现的,但那叫「造轮子」,Java 并发框架下给咱们提供了一个工具类,专门适用这种场景。框架

Semaphore 能够说是为上述这种场景而生的一个工具类,咱们写个 demo 实现上述逻辑:异步

image

执行程序以后,你会看到:工具

image

你看,出来一个线程才容许进去一个线程,这就是 Semaphore。

semaphore 的内部原理其实你去看源码,你会发现和咱们的 ReentrantLock 的实现是极其相似的,包括公平与非公平策略的支持,只不过,AQS 里面的 state 在前者的实现中,通常小于等于一(除非重入锁),然后者的 state 则小于等于十,记录的是剩余可用临界资源数量。

因此,semaphore 天生就存在一个问题,若是某个线程重入了临界区,可用临界资源的数量是否须要减小?

停车场一共十个停车位,一辆车进去并占有了一个停车位,过了一段时间,这个向管理员报告,我还要占用一个停车位,先无论他占两个干啥,此时的管理员会赞成吗?

实际上,在 Java 这个管理员看来,已经进入临界区的线程是「老爷」,提出的要求都会优先知足,即使他自身占有的资源并无释放。

因此,在 Semaphore 机制里,一个线程进入临界区以后占用掉全部的临界资源都是可能的。

倒计时门栓 CountDownLatch

下面咱们来看看这个 CountDownLatch,名字听起来挺高级,究竟提供了怎样的功能呢?

有这么一个常见的场景,咱们一块儿来看看:

你们平常常用的拼多多,一件商品至少须要两到三人拼团,商家才会发货。

这里,咱们不去研究它的商业模式,无论他是怎么实现盈利的,就这么一种场景,若是要用基本的并发 API 来实现,你可能会想到:

来一个线程阻塞一次,知道达到指定的数量后,所有唤醒

对,没错,CountDownLatch 内部就是这样实现的,轮子已经帮你造好了,咱们来看看该怎么实现上述的模型案例:

image

多运行几回,你会发现结果不会错,拼团的人前后顺序可能不一样,但商家必定是在三我的都准备好了以后才会发货。

除此以外,它还有更多的应用,好比百米赛跑,只有当全部运动员都准备好了以后,裁判员才会吹响哨子,等等等等。

实现原理也基本和显式锁相似,不一样点依然在于对 state 的控制,CountDownLatch 只判断 state 是否等于零,不等于零就说明时机未到,阻塞当前线程。

而每一次的 countDown 方法调用都会减小一次倒计时资源,直至为零才唤醒阻塞的线程。

循环屏障 CyclicBarrier

CyclicBarrier 其实和 CountDownLatch 很像,咱们先介绍完 CyclicBarrier,而后再和你一块儿去比较比较他俩的区别和类似点。

考虑这么一个场景:

公寓的班车老是在公寓楼下装满一车人以后,出发并开到地铁站,接着再回来接下一班人。

这么一个场景,咱们考虑该怎么实现:

image

效果大概就是这个样子:

image

CyclicBarrier 就像一个屏障,实例化的时候须要传入两个参数,第一个参数指定咱们的屏障最多拦截多少个线程后就打开屏障,第二个参数指明最后一个到达屏障的线程须要额外作的操做。

通常而言,最后一个线程到达屏障后,屏障将会打开,释放前面全部的线程,并在最后从新关上屏障。

CyclicBarrier 只须要用到一个 await 就能够完成全部的功能,咱们总结下该方法的实现逻辑:

  1. 首先,减小一次可用资源数量
  2. 若是可用资源数为零,则说明本身是最后一个线程,因而会执行咱们传入的额外操做,唤醒全部已经到达在等待的线程,并从新开启一个屏障计数。
  3. 不然说明本身不是最后一个线程,因而将自身线程在一个循环当中阻塞到一个条件队列上

好了,看完 CyclicBarrier 你会发现,它真的很相似咱们的倒计时门栓,下面咱们就来阐述他俩的区别与联系。

第一个区别

倒计时门栓 CountDownLatch 一旦被打开后就不能再次合上,也是说只要被调用了足够次数的 countDown,await 方法就会失效,它是一次性的。

CyclicBarrier 是循环发生的,当最后一个线程到达屏障,会优先重置屏障计数,屏障再次开启拦截阻隔。

第二个区别

CountDownLatch 是计数器, 线程来一个就记一个,此期间不阻塞线程,当达到指定数量以后才会去唤醒外部等待的线程,也就是说外部是有一个乃至多个线程等待一个条件知足以后才能继续执行,而这个条件就是知足必定数量的线程,这样才能激活当前外部线程的继续执行。

CyclicBarrier 像一个栅栏,来一个线程阻塞一个,直到阻塞了指定数量的线程后,一次性所有激活,让他们同时执行,像一个百米冲刺同样。

最后的最后

好了,以上就是咱们 Java 并发包下面比较好用的三个工具类,其中前两个的底层实现几乎彻底依赖显式锁的原理方法,后一个则是使用的显式锁加条件变量从新造的轮子,都是很是好用的工具!

除此以外还要说一点的是,整个并发这块内容,基本核心的东西咱们都已经介绍完了,共计十四篇文章,从基本的线程概念,到锁原理,到线程池,再到异步任务,自认为总结的足够细致了,不知道你了解了多少呢?

记不住不要紧,我也为你提供了一份思惟导图的总结,罗列了上述基本的内容,你能够对照着进行回顾,同时也欢迎你私信我讨论探究。

获取方式:公众号回复「并发」或是直接去个人 Github 下载。

ps:我也要放假啦,祝福你们新春快乐,春节期间就不更文了,节后咱们将开启新的篇章,系统的总结『数据库』相关的技术及原理,尽请关注!

关注公众不迷路,一个爱分享的程序员。
公众号回复「1024」加做者微信一块儿探讨学习!
每篇文章用到的全部案例代码素材都会上传我我的 github
github.com/SingleYam/o…
欢迎来踩!

YangAM 公众号
相关文章
相关标签/搜索