什么是并发工具类
思考一下?
多线程直接怎么相互协作?
- synchronizd(同步块,最简单最原始的办法):把其他多余的线程都卡住,让大家串行执行
- Wait/notify(作用在线程,在执行过程中主动等待挂起,等待其他线程给他通知notify)
- Lock/condition(作用在对象,显式的锁配合new出来的Condition对线程之间来做协调)
这些都是比较朴素的,线程之间相互协作的一种方式。(因为他不能做到我们所谓的定量,只能做定性)
因为显式的加锁操作跟实际业务杂糅到一起,使得代码看起来比较复杂。所以就创建了并发工具类。方便调用。
但更复杂的,需要这些线程满足某些条件(数量,时间)。比如:
- 我们需要控制实际并发访问资源的并发数量
- 我们需要多个线程在某一时间同时开始运行
- 我们需要指定数量线程到达某个状态再继续处理
AQS(AbstractQueuedSynchronizer)
- 队列同步器,它是构建锁或者其他同步组件的基础
如:(Semaphore、CountDownLatch、ReentrantLock、ReentrantReadWriteLock),是JUC并发包的核心基础组件,抽象了竞争的资源和线程队列。
- AbstractQueuedSynchronizer:抽象队列式的同步器
- 两种资源共享方式:独占|共享,子类负责实现公平OR非公平。
Semaphore - 信号量
使用场景:同一时间控制并发线程数
- 准入数量N,N=1则等同于独占锁(即Synchronized)
- 相当于Synchronized的进化版
信号量的其他好处:
当别的接口或者系统调用我们时,在不知道其他服务会用多少个线程或者线程池来调用我们时,可以在我们的入口增加信号量来控制,从而保护我们的服务。
CountDownLatch
阻塞主线程,N个子线程满足条件时主线程继续。
场景:Master线程等待Worker线程把任务执行完。
示例:等所有人干完手上的活儿,一起去吃饭。
示例:
CyclicBarrier
场景:
任务执行到一定阶段,等待其他任务对齐,阻塞N个线程时所有线程被唤醒继续。
示例:
等待所有人都到达,在一起开吃。
补充:
- 不是主线程await,而是各个子线程await。
- 当第N(参数:parties)个Barrier调用了await,那么整体信号的数量达到了N个,这N那大家的await就会被唤醒,继续接着往下走。
CountDownLatch和CyclicBarrier的比较
构造函数的第二个参数:barrierAction参数是达到聚合点时,要继续执行的内容。
Future/FutureTask/CompletableFuture
Future: 设置超时时间去调用get()
FutureTask:可以封装成一个有返回值的。
- 需要在当前线程里等待
- 最终还是把一个异步调用,转换成了一个同步的获取结果的一个过程
- 实际场景应用型有待提高
- Future.get()能hold住异常,然后再吐出来。但不能进行异常回调。
CompletableFuture:
- 通过回调的方式拿到异步线程执行的结果。
- 拿到异步的结果以后,需要进一步的对结果进行变换、转换、对多个线程的结果进行组合封装
- 多个线程执行,某些线程抛出异常,可以进行回调