美文网首页架构师修练手册
【并发编程】同步辅助类

【并发编程】同步辅助类

作者: 架构师修练手册 | 来源:发表于2019-05-10 00:12 被阅读0次

    在jdk1.5之后,引入了几个并发编程同步辅助类,它们都在java.util.concurrent包下,分别是CountDownLatch、CyclicBarrier、Semaphore。

    CountDownLatch

    这个工具类可以理解为计数器,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。

    例如,有任务A和B,任务A需要并发执行以提高效率,任务B则需要等待任务A完成之后才能执行,这时候就可以用该工具类。

    CountDownLatch只有一个构造函数:

    即,传入一个初始值count来确定有多少个任务(入参不得小于0,否则会抛出异常),该值存于AbstractQueuedSynchronizer类里(AQS,在《【并发编程】一文详解Java中的各种锁》中有介绍),声明为volatile,即对内存可见,保证多线程下该值同时只能被一个线程修改(关于volatile参考《【并发编程】揭开volatile的神秘面纱》),每当执行完一个任务,count减1,直到为0,所有任务执行完毕。

    重要方法

    1:await():调用此方法线程会被阻塞,直到count为0。
    2:await(long timeout, TimeUnit unit):同await(),可以设置最大等待时间,如超过最大等待时间则不再等待。
    3:countDown():count减1,直至为0。

    举个简单的例子,一个班级里有10个学生,在一场考试中,每有一个学生交卷,计数器就减1,老师想要统计学生的平均成绩就必须要等待他们全部交完试卷,即计数器为0。

    代码如下:

    打印结果:

    等待所有学生交卷
    学生1正在考试……
    学生5正在考试……
    学生9正在考试……
    学生1已交卷
    学生5已交卷
    学生0正在考试……
    学生4正在考试……
    学生4已交卷
    学生9已交卷
    学生0已交卷
    学生8正在考试……
    学生3正在考试……
    学生7正在考试……
    学生2正在考试……
    学生6正在考试……
    学生7已交卷
    学生2已交卷
    学生3已交卷
    学生6已交卷
    学生8已交卷
    全部学生已经交卷,正在计算平均分

    CyclicBarrier

    CyclicBarrier是一个可循环使用(Cyclic)的屏障(Barrier)。它能做的事情是让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会放行,所有被屏障拦截的线程继续执行。常用于多线程分组计算。

    CyclicBarrier有两个构造函数:

    CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,线程使用await()方法通知CyclicBarrier该线程已经到达了屏障,并使计数器减1,然后当前线程被阻塞直到计数器为0。

    CyclicBarrier的另一个构造函数CyclicBarrier(int parties, Runnable barrierAction),当拦截的线程数等于parties(即count等于0)时,先执行barrierAction,然后再放行被拦截的线程,方便处理更复杂的业务场景(如多线程分组计算先汇总后处理)。

    重要方法

    1、await():在CyclicBarrier上进行阻塞等待,并使count减1。
    2、await(long timeout, TimeUnit unit):在CyclicBarrier上进行限时的阻塞等待,并使count减1,当时间到达限定时间后,线程继续执行。
    3、getParties():获取CyclicBarrier通过屏障的线程数量,也称为方数。
    4、getNumberWaiting():获取正在CyclicBarrier上等待的线程数量。

    这就跟火车站载人的黑车司机一样,并不是上去一个乘客就开车,而是为了使利益最大化,等到车上乘客满了才会发一次车。

    代码如下:

    打印结果:

    乘客+1,等待满员
    乘客+1,等待满员
    乘客+1,等待满员
    乘客+1,等待满员
    乘客+1,等待满员
    乘客已经满5人,准备上车
    乘客已上车
    乘客已上车
    乘客已上车
    乘客已上车
    乘客已上车

    也就是说,当乘客满五个之后,才会执行屏障线程方法和后面的乘客上车方法,否则一直等待。

    Semaphore

    Semaphore类是一个计数信号量。计数信号量会初始化指定数量的 “许可” 。每调用一次 acquire(),一个许可就会被调用线程取走;每调用一次 release(),一个许可就会被返还给信号量。因此,在没有任何 release() 调用时,最多有 N 个线程能够通过 acquire() 方法,N 是该信号量初始化时指定的许可数量。

    Semaphore有两个构造函数:

    permits即为许可数量,默认构造函数为创建拥有permits个许可的计数信号量并设置为非公平信号量。

    如果传入fair为true,则创建拥有permits个许可的计数信号量并设置为公平信号量。

    公平信号量和非公平信号量的区别在于,当一个许可被释放后,等待的线程是随机获取许可还是按照先后顺序获取(此处可参考《【并发编程】一文详解Java中的各种锁》中公平锁与非公平锁)。

    重要方法

    1、acquire():从信号量获取1个许可,信号量内部计数器减1,如果没有许可,线程将一直阻塞。
    2、acquire(int permits):从信号量获取permits个许可,在提供这些许可前,线程一直阻塞。
    3、release():释放1个许可,将其返回给信号量,信号量内部计数器加1。
    4、release(int permits):释放permits个许可。
    5、availablePermits():当前可用的许可数。
    6、tryAcquire():尝试地获得1个许可,如果获取不到则返回false。
    7、tryAcquire(long timeout, TimeUnit unit):在指定的时间内尝试地获得1个许可,如果获取不到则返回false。
    8、tryAcquire(int permits):尝试地获得permits个许可,如果获取不到则返回false。
    9、tryAcquire(int permits, long timeout, TimeUnit unit):在指定的时间内尝试地获得permits个许可,如果获取不到则返回false。

    举个例子,一个停车场有五个车位,同时来了十辆车需要停车。

    代码如下:

    打印结果:

    来了一辆车0
    有车位,0停车入位
    来了一辆车2
    有车位,2停车入位
    来了一辆车3
    来了一辆车7
    有车位,7停车入位
    有车位,3停车入位
    来了一辆车4
    有车位,4停车入位
    来了一辆车1
    来了一辆车6
    来了一辆车8
    来了一辆车9
    来了一辆车5
    2离开车位
    7离开车位
    3离开车位
    有车位,6停车入位
    有车位,1停车入位
    有车位,8停车入位
    0离开车位
    4离开车位
    有车位,9停车入位
    有车位,5停车入位
    1离开车位
    9离开车位
    5离开车位
    8离开车位
    6离开车位

    总结

    在java.util.concurrent包下,还有很多并发编程的工具类,如atomic原子类,Lock类,ConcurrentHashMap,还有《【并发编程】Java线程池详解》中介绍的线程池等,非常值得学习。

    相关文章

      网友评论

        本文标题:【并发编程】同步辅助类

        本文链接:https://www.haomeiwen.com/subject/ykvnoqtx.html