【JUC】六、辅助类

本文介绍了Java并发工具库中的CountDownLatch(减少计数)、CyclicBarrier(循环栅栏)和Semaphore(信号灯)的使用场景、案例和原理,展示了它们如何帮助控制线程间的协作和同步。
摘要由CSDN通过智能技术生成


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

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

1、CountDownLatch减少计数

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

先不使用CountDownLatch,看下效果:

public class CountDownDemo {

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

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

在这里插入图片描述

改进,引入CountDownLatch:

public class CountDownDemo {

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

在这里插入图片描述

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颗龙珠才可以召唤神龙

public class CyclicDemo {

    //设置固定值
    private static final int NUMBER = 7;

    public static void main(String[] args) {
        //创建CyclicBarrier
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
            System.out.println("集齐7颗龙珠,召唤神龙成功!");
        });
        //集齐七颗龙珠的过程
        for (int i = 1; i <= 7 ; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "星龙珠被收集到了");
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }

    }
}

运行:

在这里插入图片描述

修改参数,让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获取许可证后,其他线程只能一直等待,阻塞状态

public class SemaphoreDemo {

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


        }
    }
}

运行:

在这里插入图片描述

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-代号9527

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值