【JAVA】CyclicBarrier源码解析以及示例

这篇具有很好参考价值的文章主要介绍了【JAVA】CyclicBarrier源码解析以及示例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


【JAVA】CyclicBarrier源码解析以及示例,java,java,开发语言,jvm

前言

在多线程编程中,同步工具是确保线程之间协同工作的重要组成部分。

CyclicBarrier(循环屏障)是Java中的一个强大的同步工具,它允许一组线程在达到某个共同点之前互相等待。

在本文中,我们将深入探讨CyclicBarrier的源码实现以及提供一些示例,以帮助您更好地理解和应用这个有趣的同步工具。


CyclicBarrier源码解析以及示例

主要成员变量
public class CyclicBarrier {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition trip = lock.newCondition();
    private final int parties;
    private int count;
    private final Runnable barrierCommand;
}
  • lock: 用于控制并发访问的重入锁。
  • trip: 条件变量,用于在屏障点上等待。
  • parties: 表示需要等待的线程数。
  • count: 表示当前已经到达屏障点的线程数。
  • barrierCommand: 在所有线程到达屏障点之后执行的命令,可以为null。
核心方法

await方法

public int await() throws InterruptedException, BrokenBarrierException {
    try {
        lock.lock();
        if (Thread.interrupted())
            throw new InterruptedException();
        int index = --count;
        if (index == 0) { // 如果是最后一个到达的线程
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier(); // 执行失败,重置屏障状态
            }
        }
        while (index > 0) {
            try {
                trip.await();
            } catch (InterruptedException ie) {
                if (index == 1 && !broken)
                    breakBarrier();
                throw ie;
            }
        }
        if (broken)
            throw new BrokenBarrierException();
        return index;
    } finally {
        lock.unlock();
    }
}

上述代码主要完成以下几个任务:

  1. 减小计数器,表示有一个线程到达了屏障点。
  2. 如果是最后一个到达的线程,执行屏障命令(如果有),然后唤醒所有等待的线程。
  3. 如果不是最后一个到达的线程,进入等待状态,直到被唤醒。
  4. 处理中断异常和屏障破坏异常。

应用场景

任务分解与合并

当一个大任务可以分解为多个子任务,每个子任务独立执行,但在某个点上需要等待所有子任务完成后再继续执行父任务。CyclicBarrier可以用来同步这些子任务的执行,确保它们在特定的屏障点上等待,然后一起继续执行。

应用示例

假设我们有一个大型的数据处理任务,需要将数据分解为若干子任务并行处理,然后在所有子任务完成后进行结果的合并。CyclicBarrier 可以用来同步子任务的执行,确保在所有子任务都完成后再进行合并操作。

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

public class TaskDecompositionAndMergeExample {

    private static final int NUM_SUBTASKS = 3;
    private static final CyclicBarrier barrier = new CyclicBarrier(NUM_SUBTASKS, () -> {
        System.out.println("All subtasks have been completed. Merging results...");
    });

    public static void main(String[] args) {
        for (int i = 0; i < NUM_SUBTASKS; i++) {
            final int subtaskId = i;
            new Thread(() -> {
                // Perform individual subtask
                System.out.println("Subtask " + subtaskId + " is processing.");

                // Simulate some computation for the subtask
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("Subtask " + subtaskId + " has completed.");

                try {
                    // Wait for all subtasks to complete
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

并行计算

在并行计算中,当多个计算节点完成局部计算后,需要将它们的结果合并。CyclicBarrier可以用来等待所有计算节点完成局部计算,然后执行合并操作。

应用示例
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class ParallelComputingExample {

    private static final int NUM_THREADS = 4;
    private static final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS, () -> {
        System.out.println("All threads have completed the computation. Merging results...");
    });

    public static void main(String[] args) {
        for (int i = 0; i < NUM_THREADS; i++) {
            final int threadId = i;
            new Thread(() -> {
                // Perform individual computation
                System.out.println("Thread " + threadId + " is performing computation.");

                // Simulate some computation for the thread
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("Thread " + threadId + " has completed computation.");

                try {
                    // Wait for all threads to complete computation
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

游戏开发

在多线程游戏开发中,可能存在多个线程分别负责不同的任务,比如渲染、物理模拟、AI计算等。

在每一帧结束时,这些线程需要同步,确保下一帧开始时所有任务都已完成。CyclicBarrier可以在每一帧结束时等待所有任务完成,然后统一开始下一帧的计算。

比如我们在打匹配游戏的时候,十个人必须全部加载到100%,才可以开局。否则只要有一个人没有加载到100%,那这个游戏就不能开始。先加载完成的玩家必须等待最后一个玩家加载成功才可以。

应用示例
public class CyclicBarrierDemo {
    private static CyclicBarrier cyclicBarrier;

    static class CyclicBarrierThread extends Thread{

        @Override
        public void run() {
            System.out.println("玩家 " + Thread.currentThread().getName() + " 加载100%");
            //等待
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args){
        cyclicBarrier = new CyclicBarrier(10, new Runnable() {
            public void run() {
                System.out.println("玩家都加载好了,开始游戏....");
            }
        });

        for(int i = 0 ; i < 10 ; i++){
            new CyclicBarrierThread().start();
        }
    }
}
输出结果
玩家 Thread-0 加载100%
玩家 Thread-2 加载100%
玩家 Thread-3 加载100%
玩家 Thread-6 加载100%
玩家 Thread-1 加载100%
玩家 Thread-4 加载100%
玩家 Thread-5 加载100%
玩家 Thread-8 加载100%
玩家 Thread-7 加载100%
玩家 Thread-9 加载100%
玩家都加载好了,开始游戏....
数据加载

在某些应用中,可能需要同时加载多个数据源,但要确保所有数据加载完成后再继续执行。CyclicBarrier可以用来等待所有数据加载完成,然后执行后续操作。

应用示例
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class DataLoaderExample {

    private static final int NUM_THREADS = 3;
    private static final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS, () -> {
        System.out.println("All data loading threads have completed. Initiating further processing...");
    });

    public static void main(String[] args) {
        for (int i = 0; i < NUM_THREADS; i++) {
            final int threadId = i;
            new Thread(() -> {
                // Simulate data loading
                System.out.println("Thread " + threadId + " is loading data.");

                // Simulate data loading time
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("Thread " + threadId + " has completed data loading.");

                try {
                    // Wait for all data loading threads to complete
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }

                // Perform further processing after data loading is complete
                System.out.println("Thread " + threadId + " is performing further processing.");
            }).start();
        }
    }
}

并发工具的协同

CyclicBarrier可以与其他并发工具一起使用,例如 ExecutorServiceCountDownLatch,以实现更复杂的多线程控制逻辑。

应用示例
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    private static final int NUM_THREADS = 3;
    private static final CyclicBarrier barrier = new CyclicBarrier(NUM_THREADS, () -> {
        System.out.println("All threads have reached the barrier. Let's continue!");
    });

    public static void main(String[] args) {
        for (int i = 0; i < NUM_THREADS; i++) {
            new Thread(() -> {
                try {
                    // Perform individual tasks
                    System.out.println(Thread.currentThread().getName() + " is performing individual tasks.");

                    // Wait for all threads to reach the barrier
                    barrier.await();

                    // Continue with collective tasks after reaching the barrier
                    System.out.println(Thread.currentThread().getName() + " is performing collective tasks.");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}


CyclicBarrier和CountDownLatch的区别

循环性:
  • CyclicBarrier 具有循环的特性,可以被重复使用。一旦所有线程都到达屏障点,它会自动重置并再次等待下一轮。这使得 CyclicBarrier 更适合用于一组线程多次协同工作的场景。
  • CountDownLatch 是一次性的,一旦计数到达零,就无法重新设置。如果需要多次等待,就需要创建新的 CountDownLatch 实例。
计数器的变化:
  • CyclicBarrier 中,计数器的递减是由到达屏障点的线程执行的,而且在所有线程都到达之前,任何线程都不会继续执行。
  • CountDownLatch 中,计数器的递减是由任意线程执行的,而且线程在递减计数器后可以继续执行,不必等待其他线程。
用途:
  • CyclicBarrier 通常用于一组线程并行执行任务,然后在某个点上等待彼此,然后再一起继续执行下一轮任务。例如,任务分解与合并、并行计算等场景。
  • CountDownLatch 用于等待一组线程完成某个任务后再执行其他任务。例如,主线程等待所有工作线程完成工作后再继续执行。
构造函数参数:
  • CyclicBarrier 的构造函数需要指定参与同步的线程数,以及在屏障点上执行的可选操作(Runnable)。
  • CountDownLatch 的构造函数需要指定计数的初始值。

专栏集锦

大佬们可以收藏以备不时之需:

Spring Boot 专栏:

ChatGPT 专栏:

Java 专栏:

Go 专栏:

Netty 专栏:

Redis 专栏:

Mysql 专栏:

架构之路 专栏:


总结

通过本文,我们深入了解了CyclicBarrier的源码实现,并通过一个简单的示例演示了它的用法。

CyclicBarrier是一个强大的同步工具,可以帮助我们实现复杂的多线程协同任务。

在多线程编程中,理解和熟练使用这样的同步工具是至关重要的,能够确保线程之间的协同工作更加高效和可靠。


写在最后

感谢您的支持和鼓励! 😊🙏

如果大家对相关文章感兴趣,可以关注公众号"架构殿堂",会持续更新AIGC,java基础面试题, netty, spring boot, spring cloud等系列文章,一系列干货随时送达!

【JAVA】CyclicBarrier源码解析以及示例,java,java,开发语言,jvm文章来源地址https://www.toymoban.com/news/detail-760491.html

到了这里,关于【JAVA】CyclicBarrier源码解析以及示例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码

    Java 语言开发的AI智慧导诊系统源码 springboot+redis 3D 互联网智导诊系统源码 智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。 智慧导诊

    2024年04月23日
    浏览(48)
  • Java设计模式:深入解析与应用示例

    设计模式是一种在特定上下文中反复出现的可重用解决方案,用于处理软件设计中常见的问题。掌握设计模式不仅可以帮助我们编写出更优雅、更易于理解和维护的代码,而且也是Java面试中的常考知识点。在本文中,我们将探讨几种常见的设计模式,包括它们的定义、使用场

    2024年02月09日
    浏览(38)
  • UUID简介以及java代码获取UUID示例

    UUID 是指(Universally Unique Identifier)通用唯一识别码,128位。RFC 4122描述了具体的规范实现。 我们开发的时候,数据库表总会有一个主键,以前我们可能会使用自增的数字作为主键。这样做去确实查询的时候比较快, 但是在做系统集成或者数据迁移的的时候就麻烦了。这是i

    2024年02月07日
    浏览(36)
  • 【Java | 多线程】可重入锁的概念以及示例

    可重入锁(又名递归锁)是一种特殊类型的锁,它允许 同一个线程在获取锁后再次进入该锁保护的代码块或方法,而不需要重新获取锁 。 说白了,可重入锁的特点就是同一个线程可以多次获取同一个锁,而不会因为之前已经获取过锁而阻塞。 可重入锁的一个优点是可以一定

    2024年04月24日
    浏览(38)
  • 短视频矩阵系统源码开发搭建技术解析-PHP语言

    一、系统架构 整个短视频矩阵系统大概分为以下几个模块: 1.多平台账号管理 支持抖音、快手、小红书、西瓜、头条视频号等多平台账号管理。 2.视频管理模块 支持视频批量上传、批量剪辑、文字转语音,特效等功能配置 3.推荐算法模块 推荐算法模块主要用来推荐用户感兴

    2024年02月07日
    浏览(70)
  • Java源码-servlet源码解析

    Servlet是运行在Web服务器上的Java组件,用于处理客户端请求并生成响应。下面将介绍Servlet的源码解析。 Servlet接口源码解析 Servlet接口是所有Servlet类必须实现的接口。该接口定义了Servlet生命周期方法和服务方法。 init方法初始化Servlet,service方法处理请求并生成响应。destroy方

    2024年02月13日
    浏览(38)
  • java源码-List源码解析

    Java中的List是一个接口,它定义了一组操作列表的方法。List接口的常见子类包括ArrayList、LinkedList和Vector等。 以下是Java中List接口及其常见方法的源码解析: 1. List接口定义 ``` public interface ListE extends CollectionE {     // 返回列表中元素的数量     int size();          // 返回列表

    2024年02月15日
    浏览(32)
  • 【Java 】从源码全面解析Java 线程池

    线程池技术在互联网技术使用如此广泛,几乎所有的后端技术面试官都要在线程池技术的使用和原理方面对小伙伴们进行 360° 的刁难。 作为一个在互联网公司面一次拿一次 Offer 的面霸,打败了无数竞争对手,每次都只能看到无数落寞的身影失望的离开,略感愧疚(请允许我

    2024年02月03日
    浏览(47)
  • Java操作elasticSearch复杂查询以及解析数据以及索引保存数据

    说明:基于银行测试库的操作 es的银行测试库,看一个Kibana操作 然后用java检索解析这个数据 聚合搜索 address 中包含 mill 的所有人的年龄分布以及平均薪资 以下是分解思路实现步骤: #聚合搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄 GET bank/_search { “query”:{ “

    2024年02月10日
    浏览(63)
  • 【JAVA原理系列】Java String类中的三个替换方法replaceFirst、replaceAll和replace原理用法示例源码详解

    这里是三种字符串替换方法的原理: replaceFirst() 方法的原理是通过编译给定的正则表达式,然后使用编译后的模式创建一个匹配器对象,将该匹配器应用于目标字符串,并使用给定的替换字符串替换第一个匹配的子字符串。 replaceAll() 方法的原理与 replaceFirst() 类似,也是通过

    2024年04月16日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包