写点什么

JUC 常用 4 大并发工具类

  • 2023-02-05
    北京
  • 本文字数:3539 字

    阅读完需:约 12 分钟

JUC 常用 4 大并发工具类

什么是 JUC?

JUC 就是 java.util.concurrent 包,这个包俗称 JUC,里面都是解决并发问题的一些定义类,该包的位置位于 java 下面的 rt.jar 包下面。

4 大常用并发工具类

  • CountDownLatch

  • CyclicBarrier

  • Semaphore

  • ExChanger

CountDownLatch

CountDownLatch使用详解


CountDownLatch,俗称 <font color=red>闭锁</font> ,作用是类似加强版的 Join,是让一组线程等待其他的线程完成工作以后才执行。


就比如在启动框架服务的时候,我们主线程需要在环境线程初始化完成之后才能启动,这时候我们就可以实现使用 CountDownLatch 来完成。


构造方法:


/*** Constructs a {@code CountDownLatch} initialized with the given count.** @param count the number of times {@link #countDown} must be invoked*        before threads can pass through {@link #await}* @throws IllegalArgumentException if {@code count} is negative*/public CountDownLatch(int count) {    if (count < 0) throw new IllegalArgumentException("count < 0");    this.sync = new Sync(count);}
复制代码


在源码中可以看到,创建CountDownLatch时,需要传入一个 int 类型的参数,将决定在执行次扣减之后,等待的线程被唤醒



方法介绍:


CountDownLatch:初始化方法


await:等待方法,同时带参数的是超时重载方法


countDown:每执行一次,计数器减一,就是初始化传入的数字,也代表着一个线程完成了任务


getCount:获取当前值


toString:这个就不用说了


里面的 Sync 是一个内部类,外面的方法其实都是操作这个内部类的,这个内部类继承了 AQS,实现的标准方法。



主线程中创建CountDownLatch(3),然后主线程 await 阻塞,然后线程 A,B,C 各自完成了任务,调用了 countDown,之后,每个线程调用一次计数器就会减一,初始是 3,然后 A 线程调用后变成 2,B 线程调用后变成 1,C 线程调用后,变成 0,这时就会唤醒正在 await 的主线程,然后主线程继续执行。

CyclicBarrier

CyclicBarrier使用


CyclicBarrier,俗称 <font color=red>栅栏锁</font> ,作用是让一组线程到达某个屏障,被阻塞,一直到组内的最后一个线程到达,然后屏障开放,接着所有的线程继续运行。


这个感觉和 CountDownLatch 有点相似,但是其实是不一样的,所谓的差别,在下面说。


构造参数:


/*** Creates a new {@code CyclicBarrier} that will trip when the* given number of parties (threads) are waiting upon it, and which* will execute the given barrier action when the barrier is tripped,* performed by the last thread entering the barrier.** @param parties the number of threads that must invoke {@link #await}*        before the barrier is tripped* @param barrierAction the command to execute when the barrier is*        tripped, or {@code null} if there is no action* @throws IllegalArgumentException if {@code parties} is less than 1*/public CyclicBarrier(int parties, Runnable barrierAction) {    if (parties <= 0) throw new IllegalArgumentException();    this.parties = parties;    this.count = parties;    this.barrierCommand = barrierAction;}
/*** Creates a new {@code CyclicBarrier} that will trip when the* given number of parties (threads) are waiting upon it, and* does not perform a predefined action when the barrier is tripped.** @param parties the number of threads that must invoke {@link #await}* before the barrier is tripped* @throws IllegalArgumentException if {@code parties} is less than 1*/public CyclicBarrier(int parties) { this(parties, null);}
复制代码


很明显能感觉出来,下面的构造参数调用了下面的构造参数,是一个构造方法重载。


首先这个第一个参数也树 Int 类型的,传入的是执行线程的个数,这个数量和 CountDownLatch 不一样,这个数量是需要和线程数量吻合的,CountDownLatch 则不一样,CountDownLatch 可以大于等于(一个线程内可以 countDown 不止一次),而 CyclicBarrier 只能等于,然后是第二个参数,第二个参数是 barrierAction,这个参数是当屏障开放后,执行的任务线程,如果当屏障开放后需要执行什么任务,可以写在这个线程中。



主线程创建CyclicBarrier(3,barrierAction),然后由线程开始执行,线程 A,B 执行完成后都调用了 await,然后他们都在一个屏障前阻塞者,需要等待线程 C 也,执行完成,调用 await 之后,然后三个线程都达到屏障后,屏障开放,然后线程继续执行,并且 barrierAction 在屏障开放的一瞬间也开始执行。


CountDownLatch 和 CyclicBarrier 的区别


CountDownLatch 的构造参数传入的数量一般都是大于等于线程数量的,因为他是由第三方控制的,可以扣减多次,CyclicBarrier 的构造参数第一个参数传入的数量一定是等于线程的个数的,因为他是由一组线程自身控制。


Semaphore

Semaphore使用详解


Semaphore,俗称 <font color=red>信号量</font> ,作用于控制同时访问某个特定资源的线程数量,用在流量控制。


构造方法:


/*** Creates a {@code Semaphore} with the given number of* permits and nonfair fairness setting.** @param permits the initial number of permits available.*        This value may be negative, in which case releases*        must occur before any acquires will be granted.*/public Semaphore(int permits) {    sync = new NonfairSync(permits);}
/*** Creates a {@code Semaphore} with the given number of* permits and the given fairness setting.** @param permits the initial number of permits available.* This value may be negative, in which case releases* must occur before any acquires will be granted.* @param fair {@code true} if this semaphore will guarantee* first-in first-out granting of permits under contention,* else {@code false} 参数fair表示是否是公平的,即等待时间越久的越先获取许可*/public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits);}
复制代码


在源码中可以看到在构建 Semaphore 信号量的时候,需要传入许可证的数量,这个数量就是资源的最大允许的访问的线程数。

Exchanger

Exchanger,俗称交换器,用于在线程之间交换数据,但是比较受限,因为只能两个线程之间交换数据。


构造方法:


/*** Creates a new Exchanger.*/public Exchanger() {    participant = new Participant();}
复制代码


构造函数没有入参,只有在创建的时候指定一下需要交换的数据的泛型即可,下面看代码


代码示例:


/** * @description: 线程之间交换数据 * @author: yh * @date: 2023/2/5 */public class UseExchange {    private static final Exchanger<Set<String>> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(){ @Override public void run() { Set<String> aSet = new HashSet<>(); aSet.add("A"); aSet.add("B"); aSet.add("C"); try { Set<String> exchange = exchanger.exchange(aSet); for (String s : exchange) { System.out.println("aSet:"+s); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start();
new Thread(){ @Override public void run() { Set<String> bSet = new HashSet<>(); bSet.add("1"); bSet.add("2"); bSet.add("3"); try { Set<String> exchange = exchanger.exchange(bSet); for (String s : exchange) { System.out.println("bSet:"+s); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start();
}}
复制代码


执行结果:


bSet:AaSet:1bSet:BaSet:2bSet:CaSet:3
复制代码


通过执行结果可以清晰的看到,两个线程中的数据发生了交换,这就是 Exchanger 的线程数据交换了。


以上就是 JUC 的 4 大常用并发工具类。

发布于: 刚刚阅读数: 8
用户头像

还未添加个人签名 2023-01-10 加入

还未添加个人简介

评论

发布
暂无评论
JUC 常用 4 大并发工具类_Java_鱼找水需要时间_InfoQ写作社区