CountDownLatch和CyclicBarrier详解

1. CountDownLatch

1.1 简介

CountDownLatch 是 Java 中并发包(java.util.concurrent)提供的一种同步工具,用于在多线程环境中协调多个线程之间的执行顺序。它的作用是允许一个或多个线程等待其他线程完成操作。

CountDownLatch 通过一个计数器来实现,计数器的初始值由用户设置,每当一个线程完成一项任务后,计数器的值就会减一。当计数器的值变为零时,等待在 CountDownLatch 上的线程就会被唤醒,可以继续执行。

以下是 CountDownLatch 的一些关键方法:

  1. CountDownLatch(int count): 构造方法,传入计数器的初始值。

  2. void countDown(): 计数器减一,表示一个线程完成了任务。

  3. void await(): 等待计数器变为零,阻塞当前线程。

  4. boolean await(long timeout, TimeUnit unit): 在指定的时间内等待计数器变为零,超时后返回 true,否则返回 false。

以下是一个简单的示例,演示了 CountDownLatch 的基本用法:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 3;
        CountDownLatch latch = new CountDownLatch(numThreads);

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                // 模拟线程执行任务
                System.out.println("Thread " + Thread.currentThread().getId() + " is executing.");
                latch.countDown(); // 任务完成,计数器减一
            }).start();
        }

        latch.await(); // 等待计数器变为零
        System.out.println("All threads have completed their tasks.");
    }
}

在这个示例中,创建了一个 CountDownLatch,并在多个线程中模拟执行任务,每个线程执行完任务后调用 countDown 方法。主线程通过 await 方法等待所有线程执行完任务后继续执行。这样,CountDownLatch 就实现了多个线程之间的协调。

CountDownLatch 的应用场景包括等待多个线程完成某个任务后再进行下一步操作、并行计算中等待多个计算任务完成、主线程等待多个子线程初始化完成等。

1.2 使用CountDownLatch模拟王者荣耀加载页面

package com.test;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 曹见朋
 * @create 2023-11-12-15:38
 */
public class CountDownLatchTest {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        CountDownLatch countDownLatch = new CountDownLatch(5);

        Random random = new Random();

        String[] all = new String[5];


        for (int i = 0; i < 5; i++) {
            int k=i;
            executorService.submit(()->{
                for (int j = 0; j <= 100; j++) {
                    try {
                        Thread.sleep(random.nextInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    all[k] = j+"%";
                    System.out.print("\r"+ Arrays.toString(all));
                }
                countDownLatch.countDown();
            });
        }


        try {
            System.out.println("玩家正在进入游戏......游戏即将开始......");
            System.out.println();
            countDownLatch.await();
            System.err.println("\n"+"\n"+"游戏开始!敌人还有3秒到达!请做好准备!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            executorService.shutdown();
        }
    }
}

在这里插入图片描述

2. CyclicBarrier

2.1 简介

CyclicBarrier 是 Java 中并发包(java.util.concurrent)提供的另一种同步工具,用于在多线程环境中实现多个线程的同步点。与 CountDownLatch 类似,CyclicBarrier 也可以用于线程的协同,但其机制略有不同。

CyclicBarrier 的主要特点是可以重用。它允许一组线程相互等待,直到所有线程都达到某个同步点,然后继续执行。一旦所有线程达到同步点,CyclicBarrier 的内部计数器会被重置,并且所有线程可以继续执行下一轮同步。

以下是 CyclicBarrier 的一些关键方法:

  1. CyclicBarrier(int parties): 构造方法,传入参与同步的线程数。

  2. int await(): 调用线程到达同步点,等待其他线程。当所有线程都调用了 await 方法后,它们就会被释放,并且 CyclicBarrier 的内部计数器会被重置。

  3. int await(long timeout, TimeUnit unit): 在指定的时间内等待其他线程。如果在超时时间内没有所有线程都到达同步点,调用线程会被释放,并返回一个表示等待状态的值。

以下是一个简单的示例,演示了 CyclicBarrier 的基本用法:

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numThreads = 3;
        CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
            // 当所有线程都到达同步点时执行的动作
            System.out.println("All threads have reached the barrier.");
        });

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                try {
                    // 模拟线程执行任务
                    System.out.println("Thread " + Thread.currentThread().getId() + " is executing.");
                    barrier.await(); // 等待其他线程到达同步点
                    System.out.println("Thread " + Thread.currentThread().getId() + " continues execution.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在这个示例中,创建了一个 CyclicBarrier,并在多个线程中模拟执行任务。每个线程执行完任务后调用 await 方法,等待其他线程到达同步点。一旦所有线程都到达同步点,就会执行在构造方法中传入的动作,并且所有线程会被释放,可以继续执行下一轮同步。

CyclicBarrier 的应用场景包括多个线程分阶段执行任务、多个线程分工合作执行任务等。

2.2 CyclicBarrier 模拟游戏多人联机对战场景

在游戏开发中,CyclicBarrier 可以用于实现多个玩家或角色在某个关键点进行同步,以确保所有玩家都准备好后再开始某个阶段的游戏。以下是一个典型的应用场景:

游戏多人联机对战场景:

假设有一个多人联机对战游戏,游戏中的每个玩家都需要准备好装备、选择角色等信息后,才能开始游戏。使用 CyclicBarrier 可以在游戏开始前创建一个同步点,确保所有玩家都准备就绪后才开始游戏。

package com.test.mythreadpool;

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

/**
 * @author 曹见朋
 * @create 2023-11-12-16:27
 */
public class CyclicBarrierTest {

    private static final int NUM_PLAYERS = 4;
    private static final CyclicBarrier gameStartBarrier = new CyclicBarrier(NUM_PLAYERS, () -> {
        System.out.println("All players are ready. Game starts!");
    });

    public static void main(String[] args) {
        for (int i = 0; i < NUM_PLAYERS; i++) {
            new Thread(() -> {
                // 模拟玩家准备工作
                System.out.println("Player " + Thread.currentThread().getId() + " is preparing.");
                try {
                    // 等待其他玩家准备
                    gameStartBarrier.await();
                    // 开始游戏
                    System.out.println("Player " + Thread.currentThread().getId() + " starts the game.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}


在这个例子中,CyclicBarrier 被用于创建一个同步点 gameStartBarrier,在这个同步点上,所有玩家等待,直到所有玩家都准备好后,执行了 gameStartBarrier.await() 后,所有玩家同时开始游戏。

这种应用场景确保了游戏开始前所有玩家的同步,避免了一些玩家在游戏开始后还在准备中的问题,提升了游戏的整体体验。

3. CyclicBarrier和CountDownLatch的区别是什么?

CyclicBarrier 和 CountDownLatch 都是Java并发包提供的用于多线程协同的工具,但它们有一些关键区别:

  1. 可重用性:

    • CyclicBarrier 是可重用的。一旦所有线程到达同步点,CyclicBarrier 的内部计数器会被重置,可以用于下一轮的同步。
    • CountDownLatch 是不可重用的。一旦计数器减为零,无法重新设置计数器,而且无法再次使用。
  2. 计数器递减:

    • CyclicBarrier 的计数器是递增的。线程调用 await 方法等待,计数器递增,直到计数器达到设定值时,所有等待的线程被唤醒。
    • CountDownLatch 的计数器是递减的。每次调用 countDown 方法,计数器减一,当计数器减为零时,等待的线程被唤醒。
  3. 等待状态:

    • 在 CyclicBarrier 中,等待的线程在同步点处被阻塞,一旦所有线程都到达同步点,它们被同时释放,继续执行下一步操作。
    • 在 CountDownLatch 中,等待的线程在计数器为零之前一直被阻塞,一旦计数器为零,所有等待的线程被唤醒,继续执行。
  4. 动作:

    • CyclicBarrier 允许在所有线程到达同步点时执行一个可选的动作(Runnable)。
    • CountDownLatch 不提供类似的机制。

下面是一个简单的比较例子:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

public class ComparisonExample {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 3;

        // CyclicBarrier 示例
        CyclicBarrier cyclicBarrier = new CyclicBarrier(numThreads, () -> {
            System.out.println("CyclicBarrier: All threads have reached the barrier.");
        });

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                System.out.println("CyclicBarrier: Thread is performing a task.");
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }

        Thread.sleep(2000); // 等待线程执行完成

        // CountDownLatch 示例
        CountDownLatch countDownLatch = new CountDownLatch(numThreads);

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                System.out.println("CountDownLatch: Thread is performing a task.");
                countDownLatch.countDown();
            }).start();
        }

        countDownLatch.await(); // 等待计数器为零

        System.out.println("CountDownLatch: All threads have completed their tasks.");
    }
}

在这个例子中,通过 CyclicBarrier 和 CountDownLatch 分别创建了两组线程,模拟了它们的使用方式。CyclicBarrier 在所有线程到达同步点时执行一个动作,而 CountDownLatch 在计数器为零时唤醒等待的线程。

最后,让我们以旅行团的情景来说明 CyclicBarrier 和 CountDownLatch 的区别:

  • CyclicBarrier(循环屏障)
    假设有一个旅行团,旅行中有多个景点需要游客参观。在出发前,导游告诉游客,每到一个景点,所有游客都需要等待其他游客到达后才能一起继续前进。导游设置了一个 CyclicBarrier,每个景点都是一个同步点。当游客到达一个景点后,他们调用 await 方法等待其他游客到达。一旦所有游客都到达,CyclicBarrier 就会打开,所有游客一起前往下一个景点。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class TravelGroupWithCyclicBarrier {
    private static final int NUM_TOURISTS = 5;
    private static final CyclicBarrier arrivalBarrier = new CyclicBarrier(NUM_TOURISTS, () -> {
        System.out.println("All tourists have arrived. Moving to the next attraction.");
    });

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TOURISTS; i++) {
            new Thread(() -> {
                // 游客到达当前景点
                System.out.println("Tourist " + Thread.currentThread().getId() + " has arrived.");
                try {
                    // 等待其他游客到达
                    arrivalBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

  • CountDownLatch
    现在,假设旅行团中的每个游客都有自己的一些准备工作,例如检查护照、购买旅行用品等。在出发前,导游告诉游客,所有准备工作完成后才能出发。导游设置了一个 CountDownLatch,初始计数器为旅客的数量。每个游客在完成准备工作后,调用 countDown 方法减少计数器。导游在等待计数器减为零后,就知道所有游客都准备好了,可以出发了。
import java.util.concurrent.CountDownLatch;

public class TravelGroupWithCountDownLatch {
    private static final int NUM_TOURISTS = 5;
    private static final CountDownLatch preparationLatch = new CountDownLatch(NUM_TOURISTS);

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TOURISTS; i++) {
            new Thread(() -> {
                // 游客完成准备工作
                System.out.println("Tourist " + Thread.currentThread().getId() + " has completed preparations.");
                // 减少准备计数器
                preparationLatch.countDown();
            }).start();
        }

        try {
            // 等待所有游客完成准备工作
            preparationLatch.await();
            System.out.println("All tourists have completed preparations. Departing now!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  • 区别总结
    • CyclicBarrier 适用于一组线程需要相互等待,达到同一点后再继续执行的场景,而且可以重复使用。
    • CountDownLatch 适用于一个或多个线程需要等待其他线程完成某个操作后再执行的场景,但它是一次性的,计数器归零后不能再次使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/135921.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java使用geotools导出shp文件

SHP格式是一种矢量数据格式&#xff0c;用于存储地理信息系统&#xff08;GIS&#xff09;数据。 SHP文件由一系列有序的文件组成&#xff0c;我们导出的shp文件包括.shp、.shx、.dbf、.prj以及.fix文件。 .shp&#xff08;shape&#xff09;文件&#xff1a;存储矢量地图数据&…

自定义类型:联合和枚举

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 联合体 1.1 联合体类型的声明 1.2 联合体的特点 1.3 相同成员的结构体和联合体对比 1.4 联合体大小的计算 1.5 联合的一个练习 2. 枚举类型 2.1 枚举类型的声明…

思维模型 目标效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。明确目标&#xff0c;激发内在动机。 1 目标效应的应用 1.1 目标效应在教育领域的应用-棉花糖实验 美国斯坦福大学心理学系的教授米歇尔&#xff08;Walter Mischel&#xff09;曾经进行了…

1204. 错误票据

题目&#xff1a; 1204. 错误票据 - AcWing题库 思路&#xff1a; 将输入的数据存入数组&#xff0c;从小到大排序后遍历&#xff0c;若 (a[i] a[i - 1])res1 a[i]--->重号;若(a[i] - a[i - 1] > 2)res2 a[i] - 1--->断号。 难点&#xff1a;题目只告诉我们输入…

1977 智慧校园平台开发与实现JSP【程序源码+文档+调试运行】

摘要 本文旨在设计和实现一个智慧校园平台系统&#xff0c;以满足管理员、教师和学生三类用户的需求。管理员拥有最高管理权限&#xff0c;可对教师和学生用户进行管理&#xff1b;教师用户可查看和管理本班学生信息、成绩等&#xff1b;学生用户可查看个人信息、考试成绩等。…

Leetcode139单词拆分及其多种变体问题

1 声明 1.1 首先&#xff0c;大家常常把这道题当作了背包问题来做&#xff0c;因为其循环结构和背包问题刚好相反&#xff0c;但事实如此嘛&#xff1f; 背包问题通常都是组合问题&#xff0c;这其实是一道“”面向目标值的排列问题“&#xff0c;具体和背包问题有什么不同可…

[01]汇川IMC30G-E系列运动控制卡应用笔记

简介 IMC30G-E系列产品是汇川技术自主研制的高性能EtherCAT网络型运动控制器&#xff08;卡&#xff09;&#xff0c;同时兼容脉冲轴的控制&#xff1b;IMC30G-E支持点位/JOG、插补、多轴同步、高速位置比较输出、PWM等全面的运动控制功能&#xff0c;具备高同步控制精度。 开发…

【Nginx】深入浅出搞懂Nginx

Nginx是一款轻量级的Web服务器、反向代理服务器&#xff0c;由于它的内存占用少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;在互联网项目中广泛应用。 反向代理服务器&#xff1f; 经常听人说到一些术语&#xff0c;如反向代理&#xff0c;那么什么是反向代理&a…

Pinia 状态管理器 菠萝:Option Store风格

Pinia介绍&#xff1a; Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 Pinia 大小只有 1kb 左右&#xff0c;超轻量级&#xff0c;你甚至可能忘记它的存在&#xff01; 相比 Vuex,Pinia 的优点&#xff1a; 更贴合 Vue 3 的 Composition API 风…

【Python】KDtree的调用

前言 查询点集中与目标点最近的k个点 from scipy.spatial import cKDTree import numpy as npdata np.array([[1,2],[1,3],[4,5]]) # 生成 100 个三维数据 tree cKDTree(data) # 创建 K-D Tree result tree.query(np.array([5, 5]), k2) # 查询与 [0.5, 0.5, 0.5] 最近的三…

Jvm虚拟机

问题&#xff1a; 计算机能识别的语言是二进制&#xff0c;Java文件是程序员编写的&#xff0c;如何能够在计算机上运行&#xff1f; 以及Java为什么可以实现跨平台&#xff1f; 一Java的jdk中有jvm虚拟机 可以将文件转换为字节码文件 使得它可以在各种平台上运行&#xff0c;这…

计算机组成原理之处理器(流水线)

引言 为什么不采用单周期实现,硬件比较简单&#xff1f; 主要是因为效率太低&#xff0c;处理器中最长的路径&#xff08;一般是ld指令)决定了时钟周期 流水线概述 流水线是一种能使多条指令重叠执行的技术。 流水线更快的原因是所有的工作都在并行执行&#xff0c;所以单位…

按键编程 pal库和标准库

按钮的电路设计 电路的搭建 原理与编程 创建了两个变量 用来捕捉按键的状态 先让两个变量都为1 previous和current都为1 &#xff08;按键没按下&#xff09; 然后让current去捕捉按键的状态通过读gpioA的pin0 如果为0就是按键按下 如果为1就是按键没按下 然后赋值给current …

论文笔记:Deep Trajectory Recovery with Fine-Grained Calibration using Kalman Filter

TKDE 2021 1 intro 1.1 背景 用户轨迹数据对于改进以用户为中心的应用程序很有用 POI推荐城市规划路线规划由于设备和环境的限制&#xff0c;许多轨迹以低采样率记录 采样的轨迹无法详细说明物体的实际路线增加了轨迹中两个连续采样点之间的不确定性——>开发有效的算法以…

哈希表之闭散列的实现

闭散列实现哈希表 在闭散列实现哈希表中&#xff0c;我们选择线性探测法来解决哈希冲突。在哈希表的简介部分&#xff0c;我们已经介绍过线性探测法啦&#xff01; 线性探测&#xff1a;从发生冲突的位置开始&#xff0c;依次向后探测&#xff0c;直到寻找到下一个空位置为止…

Carla之语义分割及BoundingBox验证模型

参考&#xff1a; Carla系列——4.Cara模拟器添加语义分割相机&#xff08;Semantic segmentation camera&#xff09; Carla自动驾驶仿真五&#xff1a;opencv绘制运动车辆的boudingbox&#xff08;代码详解&#xff09; Carla官网Bounding Boxes Carla官网创建自定义语义标签…

【离散数学必刷题】谓词逻辑(第二章 左孝凌版)刷完包过!

专栏&#xff1a;离散数学必刷题 本章需要掌握的重要知识&#xff1a; 1.利用谓词表达式表示命题 2.变元的约束 3.谓词公式的定义、谓词公式的赋值 4.谓词公式的翻译&#xff08;注意在全总个体域时使用特性谓词&#xff09; 5.有限论域上量词的消去 6.谓词公式中关于量词的等价…

软件工程——名词解释

适用多种类型的软件工程教材&#xff0c;有关名词释义的总结较为齐全~ 目录 1. 软件 2. 软件危机 3. 软件工程 4. 软件生存周期 5. 软件复用 6. 质量 7. 质量策划 8. 质量改进 9. 质量控制 10. 质量保证 11. 软件质量 12. 正式技术复审 13. ISO 14. ISO9000 15.…

思维模型 暗示效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。无形中引导他人的思想和行为。 1 暗示效应的应用 1.1 暗示效应在商业品牌树立中的应用 可口可乐的品牌形象&#xff1a;可口可乐通过广告、包装和营销活动&#xff0c;向消费者传递了一种…

macOS使用conda初体会

最近在扫盲测序的一些知识 其中需要安装一些软件进行练习&#xff0c;如质控的fastqc&#xff0c;然后需要用conda来配置环境变量和安装软件。记录一下方便后续查阅学习 1.安装miniconda 由于我的电脑之前已经安装了brew&#xff0c;所以我就直接用brew安装了 brew install …