Java并发系列之六 CyclicBarrier源码解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Java并发系列之六 CyclicBarrier源码解析

CyclicBarrier概述



CyclicBarrier字面意思是可循环使用的线程屏障。


CyclicBarrier的功能和CountDownLatch功能有点相似。都能实现线程间相互等待,直到线程做完某些任务,唤醒等待线程。那么既然他们功能类似,提供一种解决方案不就行了吗,为什么还要再提供一个呢。原因是他们的侧重点其实还不一样。在CountDownLatch中我们把线程归类为两种,一类是工作线程,一类是阻塞线程。工作线程执行完任务,可以调用countDown()方法,释放共享锁,阻塞线程则是通过await()方法 自旋获取共享锁,注意这里工作线程是不会阻塞的。在CyclicBarrier中其实只有一类线程,那就是工作线程,假设有5个工作线程,工作线程执行完任务,会判断其他4个工作线程是否执行结束,如果还有线程没有执行完,那么工作线程会阻塞,等待其他线程结束完。当其他线程都执行完了才会执行下一步,CyclicBarrier会阻塞工作线程。而且CyclicBarrier内部使用的是ReentrantLock和Condition。我们知道Condition可以让线程阻塞,并且放入到Condition的单链表中。

public class CyclicBarrierUsage {
    public static void main(String[] args) {
        int N = 5;//一共有五个线程,如果线程都执行完了
        CyclicBarrier cyclicBarrier = new CyclicBarrier(N, new Runnable() {//
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " --- after run ");
            }
        });
        for (int i = 0; i < N; i++) {//五个工作线程
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    try {
                        TimeUnit.SECONDS.sleep(1);//模拟工作
                        System.out.println(Thread.currentThread().getName() + " --- completed ");
                        cyclicBarrier.await();//执行完工作 等待其他线程完成
                        System.out.println(Thread.currentThread().getName() + " --- run again ");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

输出结果


Thread-2 — completed


Thread-1 — completed


Thread-0 — completed


Thread-4 — completed


Thread-3 — completed


Thread-3 — after run


Thread-3 — run again


Thread-2 — run again


Thread-0 — run again


Thread-4 — run again


Thread-1 — run again


构建CyclicBarrier的时候我们往构造函数传递了一个Runnable对象,这个Runnable会在线程屏障点到达的时候,被刚好到达屏障点的那个线程执行。所以它是在工作线程中执行的。如果我们在Android开发中,需要在UI线程中执行Runnable还需要转到UI线程执行


源码解析


构造函数

 public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;//线程个数
        this.count = parties;
        this.barrierCommand = barrierAction;//到达屏障点 需要执行的任务
    }
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

成员变量

/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/** The number of parties */
private final int parties;
/* The command to run when tripped */
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();

通过成员变量我们可以知道内部使用的就是ReentrantLock和Condition


await()

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

接下来我们详细讲解下dowait(),基本上精华都在里面了

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();//独占锁上锁
        try {
            final Generation g = generation;
            if (g.broken)
                throw new BrokenBarrierException();
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            int index = --count;//线程获取到锁了,count-1
            if (index == 0) {  // index=0表示所有线程都执行过了,触发屏障
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();//执行Runnable
                    ranAction = true;
                    nextGeneration();//nextGeneration会调用trip.signalAll(),唤醒所有等待在trip上的线程
                    return 0;
                } finally {
                    if (!ranAction)//如果抛异常了 唤醒所有等待在trip上的线程
                        breakBarrier();
                }
            }
            // 如果index!=0表示还有其他线程没有执行过,那么调用trip.await(),让当前线程阻塞
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }
                if (g.broken)
                    throw new BrokenBarrierException();
                if (g != generation)
                    return index;
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }


相关文章
|
12天前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
6天前
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
18 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
12天前
|
缓存 负载均衡 Dubbo
Dubbo技术深度解析及其在Java中的实战应用
Dubbo是一款由阿里巴巴开源的高性能、轻量级的Java分布式服务框架,它致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
39 6
|
9天前
|
监控 算法 Java
深入解析Java中的垃圾回收机制
本文旨在全面解析Java的垃圾回收机制,探讨其工作原理、常见算法以及在实际开发中的应用。通过对这一重要主题的深入分析,希望帮助读者更好地理解Java虚拟机(JVM)如何管理内存,从而编写出更高效、稳定的Java应用程序。
|
9天前
|
Java 开发者
Java中的异常处理机制深度解析
在Java编程中,异常处理是保证程序稳定性和健壮性的重要手段。本文将深入探讨Java的异常处理机制,包括异常的分类、捕获与处理、自定义异常以及一些最佳实践。通过详细讲解和代码示例,帮助读者更好地理解和应用这一机制,提升代码质量。
12 1
|
10天前
|
分布式计算 Java API
深入解析Java中的Lambda表达式及其应用
本文将深入探讨Java中Lambda表达式的定义、优势及其在实际编程中的应用。通过具体示例,帮助读者更好地理解和使用这一强大的编程工具。
|
2月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
2月前
|
存储 NoSQL Redis
redis 6源码解析之 object
redis 6源码解析之 object
58 6
|
21天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理
|
25天前
|
开发工具
Flutter-AnimatedWidget组件源码解析
Flutter-AnimatedWidget组件源码解析

热门文章

最新文章

推荐镜像

更多
下一篇
无影云桌面