• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Y-wee
博客园    首页    新随笔    联系   管理     

JUC 常见三大辅助类

JUC 常见三大辅助类

CountDownLatch(减少计数)

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待

用给定的计数 初始化 CountDownLatch,由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞;之后,会释放所有等待的线程,await 的所有后续调用都将立即返回;这种现象只出现一次——计数无法被重置,如果需要重置计数,请考虑使用 CyclicBarrier

CountDownLatch 是一个通用同步工具,它有很多用途:将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口,在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待;用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待

CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行减 1 的操作,计数器等于 0之前调用 await 方法线程处于等待状态直到计数器等于0

总结:

  • CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,这些线程会阻塞
  • 其它线程调用 countDown 方法会将计数器减 1(调用 countDown 方法的线程不会阻塞)
  • 当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行

代码实战

场景:教室有7 个同学,当所有同学陆续离开教室后值班同学才可以关门

package com.yl.assist;

import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch使用案例
 *
 * @author Y-wee
 */
public class CountDownLatchTest {

    public static void main(String[] args) {
        // 计数器(设置教室剩余学生数量)
        CountDownLatch count = new CountDownLatch(7);

        for (int i = 1; i <= 7; i++) {
            new Thread(() -> {
                // 计数器减一(学生离开,教室剩余学生数量减一)
                count.countDown();

                System.out.println(Thread.currentThread().getName() + "离开了教室");
            }, "student" + i).start();
        }

        try {
            // 计数器等于0(教室剩余学生数量为0)之前main线程等待
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 计数器等于0,main线程被唤醒(教室剩余学生数量为0-教室关门)
        System.out.println("教室关门");

    }

}

执行结果

student1离开了教室
student2离开了教室
student3离开了教室
student4离开了教室
student5离开了教室
student6离开了教室
student7离开了教室
教室关门

CyclicBarrier(循环栅栏)

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)

在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用,因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier

CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后的语句,可以将 CyclicBarrier 理解为加 1 操作

代码实战

场景:集齐 7 颗龙珠就可以召唤神龙

package com.yl.assist;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrierTest使用案例
 *
 * @author Y-wee
 */
public class CyclicBarrierTest {
    private final static int NUMBER = 7;

    public static void main(String[] args) {
        /*
        创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动
        并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行
        (集齐七颗龙珠才可以召唤神龙)
         */
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()-> System.out.println("集齐七颗龙珠,召唤神龙成功"));

        for (int i = 1; i <= 7; i++) {
            new Thread(() -> {
                // 模拟收集龙珠
                System.out.println(Thread.currentThread().getName()+"集到了");
                try {
                    // 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待(龙珠集齐之前处于等待状态)
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            }, "龙珠" + i).start();
        }

    }

}

执行结果

龙珠1集到了
龙珠2集到了
龙珠3集到了
龙珠4集到了
龙珠5集到了
龙珠6集到了
龙珠7集到了
集齐七颗龙珠,召唤神龙成功

Semaphore(信号灯)

一个计数信号量,从概念上讲,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可,每个 release() 释放一个许可,从而可能释放一个正在阻塞的获取者;Semaphore 只对可用许可的号码进行计数,并采取相应的行动

Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目

总结:Semaphore维护了一个许可集,当线程未获取到许可时处于等待状态,直到线程获取到许可

代码实战

场景:抢车位,7 部汽车 3 个停车位

package com.yl.assist;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * Semaphore使用案例
 *
 * @author Y-wee
 */
public class SemaphoreTest {

    public static void main(String[] args) {
        // 设置信号量(许可集),模拟三个停车位
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <= 7; i++) {
            new Thread(() -> {
                try {
                    // 获取信号量(停车许可)
                    semaphore.acquire();

                    System.out.println(Thread.currentThread().getName() + "进入了停车位");

                    // 线程等待(停车3s)
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    /*
                    注意:离开停车位的输出语句需要写在释放信号量之前,不然可能造成信号量(停车许可释放了),
                    但是车子还没有离开,此时,另一个线程(车子)获取到了停车许可进来了
                     */
                    System.out.println(Thread.currentThread().getName() + "离开了停车位");

                    // 释放信号量(停车许可)
                    semaphore.release();
                }
            }, "车辆" + i).start();
        }
    }

}

执行结果

车辆1进入了停车位
车辆3进入了停车位
车辆4进入了停车位
车辆3离开了停车位
车辆1离开了停车位
车辆5进入了停车位
车辆6进入了停车位
车辆4离开了停车位
车辆7进入了停车位
车辆5离开了停车位
车辆6离开了停车位
车辆2进入了停车位
车辆7离开了停车位
车辆2离开了停车位
记得快乐
posted @ 2022-05-29 17:29  Y-wee  阅读(94)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3