CountDownLatch和CyclicBarrier详解

news2024/11/19 20:34:57

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 适用于一个或多个线程需要等待其他线程完成某个操作后再执行的场景,但它是一次性的,计数器归零后不能再次使用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python基础入门例程51-NP51 列表的最大与最小(循环语句)

最近的博文&#xff1a; Python基础入门例程50-NP50 程序员节&#xff08;循环语句&#xff09;-CSDN博客 Python基础入门例程49-NP49 字符列表的长度-CSDN博客 Python基础入门例程48-NP48 验证登录名与密码&#xff08;条件语句&#xff09;-CSDN博客 目录 最近的博文&…

深度解析CompletableFuture:Java 异步世界的奇迹

目录 概述 介绍 上文我们可知&#xff1a;CompletableFuture 是 Java 8 引入用于支持异步编程和非阻塞操作的类。对于没有使用过CompletableFuture通过它这么长的名字就感觉到一头雾水&#xff0c;那么现在我们来一起解读一下它的名字。 Completable&#xff1a;可完成Futur…

【Redis】Redis-Key的使用

上一篇&#xff1a; redis-server和redis-cli https://blog.csdn.net/m0_67930426/article/details/134361885?spm1001.2014.3001.5501 官网 命令 |雷迪斯 (redis.io) 设置key set name xxxxx 查看key keys * 再设置一个key并且查看 这里查看了两个key&#xff08;name a…

Git之分支与版本

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 &#xff0c;越幸运。 1.开发测试上线git的使用 1.1. 环境讲述 当软件从开发到正式环境部署的过程中&#xff0c;不同环境的作用…

《015.SpringBoot+vue之音乐网》【前后端分离】

《015.SpringBootvue之音乐网》【前后端分离】 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatisMySQL; 前台&#xff1a;Vue3.0 TypeScript Vue-Router Vuex Axios …

Globalsign证书

GlobalSign是全球可信的数字证书提供商之一&#xff0c;提供广泛的证书服务&#xff0c;包括SSL证书、代码签名证书、电子邮件证书等&#xff0c;帮助保护企业和个人的网络安全。本文将详细介绍GlobalSign证书的特点和优势&#xff0c;帮助您更好地了解这一重要的数字证书提供商…

高防CDN:护航网络安全的卓越之选

在当今数字化时代&#xff0c;网络攻击与日俱增&#xff0c;为了确保网站和应用程序的稳定运行&#xff0c;高防CDN&#xff08;高防御内容分发网络&#xff09;应运而生。选择高防CDN的理由不仅源于其强大的防护性能&#xff0c;还体现了其与硬件防火墙异曲同工的奥妙。 选择高…

MYSQL字符串函数详解和实战(字符串函数大全,内含示例)

MySQL提供了许多字符串函数&#xff0c;用于处理和操作字符串数据。以下是一些常用的MYSQL字符串函数。 建议收藏以备后续用到查阅参考。 目录 一、CONCAT 拼接字符串 二、CONCAT_WS 拼接字符串 三、SUBSTR 取子字符串 四、SUBSTRING 取子字符串 五、SUBSTRING_INDEX 取子…

Git的原理与使用(一)

目录 Git初始 Git安装 Git基本操作 创建git本地仓库 配置git 工作区,暂存区,版本库 添加文件,提交文件 查看.git文件 修改文件 版本回退 小结 Git初始 git是一个非常强大的版本控制工具.可以快速的将我们的文档和代码等进行版本管理. 下面这个实例看理解下为什么需…

eocc1_Findings_candlestick_ohlc_volume_

An Unusually Tall Candle Often Has a Minor High or Minor Low Occurring within One Day of It异常高的蜡烛通常会在一天内出现小幅高点或小幅低点 I looked at tens of thousands of candles to prove this, and the study details are on my web site, ThePatternSite.com…

软件工程——名词解释

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

SpringBoot系列-2 自动装配

背景&#xff1a; Spring提供了IOC机制&#xff0c;基于此我们可以通过XML或者注解配置&#xff0c;将三方件注册到IOC中。问题是每个三方件都需要经过手动导入依赖、配置属性、注册IOC&#xff0c;比较繁琐。 基于"约定优于配置"原则的自动装配机制为该问题提供了一…

macOS使用conda初体会

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

【yolov5】onnx的INT8量化engine

GitHub上有大佬写好代码&#xff0c;理论上直接克隆仓库里下来使用 git clone https://github.com/Wulingtian/yolov5_tensorrt_int8_tools.git 然后在yolov5_tensorrt_int8_tools的convert_trt_quant.py 修改如下参数 BATCH_SIZE 模型量化一次输入多少张图片 BATCH 模型量化…

Technology Strategy Patterns 学习笔记8- Communicating the Strategy-Decks(ppt模板)

1 Ghost Deck/Blank Deck 1.1 It’s a special way of making an initial deck that has a certain purpose 1.2 you’re making sure you have figured out what all the important shots are before incurring the major expense of shooting them 1.3 需要从技术、战略、产…

2023 年最新企业微信官方会话机器人开发详细教程(更新中)

目标是开发一个简易机器人&#xff0c;能接收消息并作出回复。 获取企业 ID 企业信息页面链接地址&#xff1a;https://work.weixin.qq.com/wework_admin/frame#profile 自建企业微信机器人 配置机器人应用详情 功能配置 接收消息服务器配置 配置消息服务器配置 配置环境变量…

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

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

OpenWRT浅尝 / 基于RAVPower-WD009便携路由文件宝的旁路网关配置

目录 前言需求分析手头的设备家庭网络拓扑图旁路网关配置OpenWRT固件选择OpenWRT固件刷入旁路网关配置流程 旁路网关的使用前置工作日常存储/关键备份内网穿透24小时待命下载器 前言 近期由于个人需求&#xff0c;需要一台OpenWRT设备实现一些功能。所以本文主要还是为了自己后…

k8s-实验部署 1

1、k8s集群部署 更改所有主机名称和解析 开启四台实验主机&#xff0c;k8s1 仓库&#xff1b;k8s2 集群控制节点&#xff1b; k8s3 和k8s4集群工作节点&#xff1b; 集群环境初始化 使用k8s1作为仓库&#xff0c;将所有的镜像都保存在本地&#xff0c;不要将集群从外部走 仓库…

金和OA jc6 任意文件上传漏洞复现

0x01 产品简介 金和OA协同办公管理系统软件&#xff08;简称金和OA&#xff09;&#xff0c;本着简单、适用、高效的原则&#xff0c;贴合企事业单位的实际需求&#xff0c;实行通用化、标准化、智能化、人性化的产品设计&#xff0c;充分体现企事业单位规范管理、提高办公效率…