在上一篇中,我们讲了lock的出现是解决syncroinzed使用不灵活的问题,其实syncroinzed是使用wait/notify实现线程间通信,那J.U.C 里面有没有提供类似的线程通信的工具呢?我们今天就来聊下这些工具以及它的底层原理,在聊之前,我先总结成一句话:所有这些工具底层本质归结到AQS队列(同步队列)以及Condition队列(等待队列),接下来看如何印证这句话!
补充:AQS在实现独占锁的时候,state是锁标记(只有一个线程可以对次变量进行修改)!在实现共享锁的时候,state只是计数器(允许多个线程对其操作)!
并发工具1:Condition--》一个多线程协调通信的工具类,可以让某些线程一起等待某个条件(condition),只有满足条件时,线程才会被唤醒!condition 中两个最重要的方法,一个是 await,一个是 signal 方法!await:把当前线程阻塞挂起,signal:唤醒阻塞的线程!使用的方式原理也与wait/notify类似!


并发工具2:CountDownLatch--》countdownlatch 是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完毕再执行!或者可以理解为:“可以使得一组线程达到一个同步点之前阻塞”!有点类似 join 的功能,但是比 join 更加灵活!
原理:CountDownLatch使用的是AQS的共享功能!CountDownLatch 构造函数会接收一个 int 类型的参数state作为计数器的初始值,然后调用await方法,await 可以被多个线程调用,所有调用了await 方法的线程阻塞在 AQS 的阻塞队列中,等待条件满足(state == 0),将线程从队列中一个个唤醒过来。而这个时候如果执行countDown方法,每次调用都会将 state 减 1,直到state为0的时候,则调用 doReleaseShared唤醒处于 await 状态下的线程!这里需要注意一点,共享锁的释放和独占锁的释放还是有区别的,它之前会把共享锁模式下的结点状态变为PROPAGATE,处于这个状态下的节点,会对线程的唤醒进行传播!

案例分析:

运行结果:

并发工具3:Semaphore--》可以控制同时访问的线程个数,通过 acquire 获取一个许可,如果没有就等待,通过 release 释放一个许可!本质上讲是操作系统中的信号量的概念!常用的场景:限流!
原理分析:实现一定是基于 AQS 的共享锁,创建 Semaphore 实例的时候,需要一个参数 permits,这个基本上可以确定是设置给 AQS 的 state 的,然后每个线程调用 acquire 的时候,执行 state = state - 1!release 的时候执行 state = state + 1,当然,acquire 的时候,如果 state=0,说明没有资源了,需要等待其他线程 release,具体的实现可以查看官方文档或者网上一些博主的记录,因为类似于信号量,所以比较好理解,这里就不实现了
注意点:Semaphore分公平策略和非公平策略,区别就在于是不是会先判断是否有线程在排队,然后才进行 CAS 减操作!如果有判断则是公平策略,否则是非公平策略!

并发工具4:CyclicBarrier(可循环使用的屏障)--》它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。CyclicBarrier 默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 当前线程已经到达了屏障,然后当前线程被阻塞!
使用场景:当存在需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用 CyclicBarrier!
原理:CyclicBarrier 相比 CountDownLatch 来说,要简单很多,源码实现是基于 ReentrantLock 和 Condition 的组合使用。
使用案例:从不同地方导入数据,然后进行分析

