【JUC】六、辅助类

客官°小女子只卖身不卖艺 2024-02-18 10:41 137阅读 0赞

文章目录

  • 1、CountDownLatch减少计数
  • 2、CyclicBarrier循环栅栏
  • 3、Semaphore信号灯

本篇整理JUC的几个同步辅助类:

  • 减少计数:CountDownLatch
  • 循环栅栏:CyclicBarrier
  • 信号灯:Semaphore

1、CountDownLatch减少计数

案例:6个同学陆续离开教室后,班长才可以锁门

先不使用CountDownLatch,看下效果:

  1. public class CountDownDemo {
  2. public static void main(String[] args) {
  3. for (int i = 1; i < 7; i++) {
  4. new Thread(() -> {
  5. System.out.println(Thread.currentThread().getName() + "号同学离开了教室");
  6. },String.valueOf(i)).start();
  7. }
  8. System.out.println(Thread.currentThread().getName() + " 班长锁门走人了");
  9. }
  10. }

运行:这里循环执行6次start,创建6个就绪状态的线程后,main线程继续向下执行,而这6个新线程还要抢夺CPU时间片、执行,因此可能出现其他线程未执行结束,main线程(班长)就执行结束的情况。(先main线程中start创建线程,再抢时间片,抢到后才执行run方法,不要迷)

在这里插入图片描述

改进,引入CountDownLatch:

  1. public class CountDownDemo {
  2. public static void main(String[] args) throws InterruptedException {
  3. //创建CountDownLatch对象,设置初始值
  4. CountDownLatch countDownLatch = new CountDownLatch(6);
  5. for (int i = 1; i < 7; i++) {
  6. new Thread(() -> {
  7. System.out.println(Thread.currentThread().getName() + "号同学离开了教室");
  8. //计数减一(因为到这儿,新线程的run方法也执行完了)
  9. countDownLatch.countDown();
  10. },String.valueOf(i)).start();
  11. }
  12. //计数器未成0前,让当前线程(main线程)挂起
  13. countDownLatch.await();
  14. System.out.println(Thread.currentThread().getName() + " 班长锁门走人了");
  15. }
  16. }

在这里插入图片描述

CountDownLatch类的常用方法

  • 构造器方法,传入初始的计数

    CountDownLatch(int count)

  • 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断

    void await()

  • 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间

    boolean await(long timeout, TimeUnit unit)

  • 锁存器计数减一

    void countDown()

  • 返回当前计数

    long getCount()

总结

  • 定义计数器初始值
  • 每次相关操作后,让计数器减一
  • 计数器的值没变成0 ,当前线程就一直await,等计数器变成0后,await后面的代码才会继续执行

2、CyclicBarrier循环栅栏

作用

让一组线程互相等待,直到达到某个公共的屏障点。且可以在达到这个公共屏障点后(在一组线程中的最后一个线程到达之后,但在释放所有线程之前)执行一个Runnable。注意不是新开一个线程去执行,而是由最后一个进入 barrier 的线程执行。

案例:集齐7颗龙珠才可以召唤神龙

  1. public class CyclicDemo {
  2. //设置固定值
  3. private static final int NUMBER = 7;
  4. public static void main(String[] args) {
  5. //创建CyclicBarrier
  6. CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
  7. System.out.println("集齐7颗龙珠,召唤神龙成功!");
  8. });
  9. //集齐七颗龙珠的过程
  10. for (int i = 1; i <= 7 ; i++) {
  11. new Thread(() -> {
  12. try {
  13. System.out.println(Thread.currentThread().getName() + "星龙珠被收集到了");
  14. cyclicBarrier.await();
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. },String.valueOf(i)).start();
  19. }
  20. }
  21. }

运行:

在这里插入图片描述

修改参数,让for循环跑6次(即只创建6个线程去调用await,刻意不让其到达number),而number仍然为7,此时运行,程序一直等待,最后exit -1

在这里插入图片描述
CyclicBarrier类的常用方法

  • 构造方法,传入参与线程的数量,和最后要执行的操作

    CyclicBarrier(int parties, Runnable barrierAction)

  • 在所有参与线程未调用CyclicBarrier对象的await方法之前,将一直等待,也就是上面演示的达到不了那个number。这些参与的线程,在其最后调用await,就相当于在说我已在当前屏障出等待。而若有参与线程不调用await,就像春游发车前少个人一样,只能整个线程组都等着。

    int await()

  • 同上,未到屏障点前,所有调用了await的线程(参与线程)就一直等待,除非到了超时时间

    await(long timeout, TimeUnit unit)

  • 返回当前已到达屏障点的线程的数量(春游实际到上车点的人数)

    int getNumberWaiting()

  • 返回传入的屏障点(春游报名人数)

    int getParties()

总结

Barrier,屏障,参与的线程调一次await,即说明该线程已在屏障出等待,屏障点+1,达到这个屏障点,线程组的所有线程才继续往下执行,否则之前调用了await方法的线程就一直处于await等待状态,除非到了指定的超时时间

3、Semaphore信号灯

作用

维护一个信号量,这个信号量里有多个许可,拿到许可就执行,没拿到就等着。有点像对象锁了,而和对象锁不同的是,一个对象,一把对象锁,但Semaphore的这个锁(许可)的数量是你自己传入的。

案例:6辆汽车,停三个停车位

车停在车位A,其他车就不能再停在A位了,这就是获取一个许可。车开出去,就是释放一个许可

acquire获取许可证后,其他线程只能一直等待,阻塞状态

  1. public class SemaphoreDemo {
  2. public static void main(String[] args) {
  3. //创建Semaphore,设置许可数量,三个车位,对应三个许可证
  4. Semaphore semaphore = new Semaphore(3);
  5. //模拟6辆汽车
  6. for (int i = 1; i <= 6; i++) {
  7. new Thread(() -> {
  8. try {
  9. //抢占许可证
  10. semaphore.acquire();
  11. System.out.println(Thread.currentThread().getName() + "抢到了车位");
  12. //设置一个5s以内的随机时间,模拟停车
  13. TimeUnit.SECONDS.sleep(new Random().nextInt(5));
  14. System.out.println(Thread.currentThread().getName() + "=====> 离开了车位");
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. } finally {
  18. //释放许可
  19. semaphore.release();
  20. }
  21. },String.valueOf(i)).start();
  22. }
  23. }
  24. }

运行:

在这里插入图片描述

Semaphore类的常用方法

  • 构造方法,传入一个数字,如果把Semaphore类看成我们前面并发编程步骤里的资源类,那这个数字就是设置有几把对象锁

    Semaphore(int permits)

  • 构造方法重载,可传入一个Boolean,表示是否公平的给许可证,设置false时,是允许新启动的线程插队的,设置为true,则按照先进先出的方式来在等待的线程队列中选择线程,发放许可

    Semaphore(int permits, boolean fair)

  • 获取许可

    void acquire()

  • 释放许可

    void release()

在这里插入图片描述

总结

个人理解就是,如果把Semaphore类看成我们前面并发编程步骤里的资源类,那它特殊的地方就是,这个资源类的一个对象有几把对象锁,是我们可以自己在构造方法里设置的,而普通的自定义资源类,想要n把对象锁,就得new上n个对象。


最后,API文档地址:https://tool.oschina.net/apidocs/apidoc?api=jdk-zh

发表评论

表情:
评论列表 (有 0 条评论,137人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JUC强大的辅助

    JUC中提供了三种常用的辅助类,通过这些辅助类可以很好的解决线程数量过多时Lock锁的频繁操作。这三种辅助类为: • CountDownLatch: 减少计数 • Cy

    相关 JUC强大的辅助工具

    JUC中提供了一些辅助类,通过这些辅助类可以很好的解决线程数量过多时Lock锁的频繁操作。常用的三种辅助类有: • CountDownLatch: 减少计数 • Cycli

    相关 辅助工具

    1:项目管理软件,能随时随地看到项目的进展,以及bug的反馈等等信息 > [http://www.zentao.net/][http_www.zentao.net]  禅道