资讯专栏INFORMATION COLUMN

(九)java多线程之CyclicBarrier

lingdududu / 3112人阅读

摘要:因为该在释放等待线程后可以重用,所以称它为循环的。若在继续所有参与线程之前更新共享状态,此屏障操作很有用。返回要求启动此的参与者数目。查询此屏障是否处于损坏状态。将屏障重置为其初始状态。

本人邮箱:
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载

引言

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

理论

CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
示例用法:下面是一个在并行分解设计中使用 barrier 的例子:

class Solver {

final int N;
final float[][] data;
final CyclicBarrier barrier;

class Worker implements Runnable {
    int myRow;

    Worker(int row) {
        myRow = row;
    }

    public void run() {
        while (!done()) {
            processRow(myRow);
            try {
                barrier.await();
            } catch (InterruptedException ex) {
                return;
            } catch (BrokenBarrierException ex) {
                return;
            }
        }
    }
}

public Solver(float[][] matrix) {
    data = matrix;
    N = matrix.length;
    barrier = new CyclicBarrier(N,
            new Runnable() {
                public void run() {
                    mergeRows(...);
                }
            });
    for (int i = 0; i < N; ++i)
        new Thread(new Worker(i)).start();

    waitUntilDone();
}

}

在这个例子中,每个 worker 线程处理矩阵的一行,在处理完所有的行之前,该线程将一直在屏障处等待。处理完所有的行之后,将执行所提供的 Runnable 屏障操作,并合并这些行。如果合并者确定已经找到了一个解决方案,那么 done() 将返回 true,所有的 worker 线程都将终止。

如果屏障操作在执行时不依赖于正挂起的线程,则线程组中的任何线程在获得释放时都能执行该操作。为方便此操作,每次调用 await() 都将返回能到达屏障处的线程的索引。然后,您可以选择哪个线程应该执行屏障操作,例如:

if (barrier.await() == 0) {

 // log the completion of this iteration

}
对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。

内存一致性效果:线程中调用 await() 之前的操作 happen-before 那些是屏障操作的一部份的操作,后者依次 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。

CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。

CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。

await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。

await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。

getNumberWaiting() 返回当前在屏障处等待的参与者数目。

getParties() 返回要求启动此 barrier 的参与者数目。

isBroken() 查询此屏障是否处于损坏状态。

reset() 将屏障重置为其初始状态。

例子

还是一样,理论是比较枯燥的,咱们还是举例来说,比较生动一点.一些教科书举一些1~100000分批累加例子.咱们不举这种比较无聊的例子.旅游,相信很多人都喜欢吧!旅游的套路一般都出发点集合,入住酒店,到旅游点1,再到旅游点2,再到旅游点3,在集合返回.每次都某一个地点时.导游都会清点人数.不要把人给弄丢.ok,开始编码...

首先写一个旅游类 TourismRunnable,run方法很简单就调用tourism()旅游的行程,然后在tourism,再调用旅游点路程,看代码..

public class TourismRunnable implements Runnable{
    CyclicBarrier cyclicBarrier;
    Random random;
    public TourismRunnable(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
        this.random = new Random();
    }

    @Override
    public void run() {
        tourism();
    }

    /**
     * 旅游过程
     */
    private void tourism() {
        goToStartingPoint();
        goToHotel();
        goToTourismPoint1();
        goToTourismPoint2();
        goToTourismPoint3();
        goToEndPoint();
    }

    /**
     * 装备返程
     */
    private void goToEndPoint() {
        goToPoint("飞机场,准备登机回家");
    }

    /**
     * 到达旅游点3
     */
    private void goToTourismPoint3() {
        goToPoint("旅游点3");
    }

    /**
     * 到达旅游点2
     */
    private void goToTourismPoint2() {
        goToPoint("旅游点2");
    }

    /**
     * 到达旅游点1
     */
    private void goToTourismPoint1() {
        goToPoint("旅游点1");
    }

    /**
     * 入住酒店
     */
    private void goToHotel() {
        goToPoint("酒店");
    }

    /**
     * 出发点集合
     */
    private void goToStartingPoint() {
        goToPoint("出发点");
    }

    private int getRandomTime(){
        int time = this.random.nextInt(400) + 100;
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return time;
    }

    private void goToPoint(String point){
        try {
            String name = Thread.currentThread().getName();
            System.out.println(name + " 花了 " + getRandomTime() + " 时间才到了" + point);
            cyclicBarrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在每个旅游点,每个人旅游所花的时间是随机的,有些人玩的比较久一点,有些则走马观花,拍拍照就完事了

再写一个测试类,让一群小朋友去旅游吧

public class TestMain {

    public static void main(String[] args) {
        String name = "明刚红丽黑白";
        CyclicBarrier cyclicBarrier = new CyclicBarrier(name.length(), new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() +
                        " 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....");
            }
        });
        List tourismThread = new ArrayList<>();
        for (char ch : name.toCharArray()){
            tourismThread.add(new Thread(new TourismRunnable(cyclicBarrier), "小" + ch));
        }
        for (Thread thread : tourismThread){
            thread.start();
        }
    }
}

运行结果:

小刚 花了 131 时间才到了出发点
小黑 花了 237 时间才到了出发点
小丽 花了 250 时间才到了出发点
小红 花了 335 时间才到了出发点
小明 花了 379 时间才到了出发点
小白 花了 398 时间才到了出发点
小白 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....
小红 花了 128 时间才到了酒店
小刚 花了 156 时间才到了酒店
小黑 花了 240 时间才到了酒店
小白 花了 280 时间才到了酒店
小明 花了 492 时间才到了酒店
小丽 花了 499 时间才到了酒店
小丽 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....
小丽 花了 188 时间才到了旅游点1
小刚 花了 315 时间才到了旅游点1
小明 花了 374 时间才到了旅游点1
小白 花了 395 时间才到了旅游点1
小黑 花了 428 时间才到了旅游点1
小红 花了 496 时间才到了旅游点1
小红 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....
小明 花了 206 时间才到了旅游点2
小刚 花了 223 时间才到了旅游点2
小红 花了 302 时间才到了旅游点2
小白 花了 308 时间才到了旅游点2
小黑 花了 317 时间才到了旅游点2
小丽 花了 400 时间才到了旅游点2
小丽 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....
小白 花了 100 时间才到了旅游点3
小丽 花了 132 时间才到了旅游点3
小红 花了 157 时间才到了旅游点3
小黑 花了 165 时间才到了旅游点3
小刚 花了 375 时间才到了旅游点3
小明 花了 416 时间才到了旅游点3
小明 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....
小刚 花了 100 时间才到了飞机场,准备登机回家
小黑 花了 137 时间才到了飞机场,准备登机回家
小红 花了 232 时间才到了飞机场,准备登机回家
小明 花了 260 时间才到了飞机场,准备登机回家
小丽 花了 264 时间才到了飞机场,准备登机回家
小白 花了 394 时间才到了飞机场,准备登机回家
小白 清点人数,1,2,3...,ok,人到齐了,准备出发..... go go go....

ok, 运行结果一目了然,中途没有落下任何一个人!CyclicBarrier这个类,还是比较容易使用的

打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69921.html

相关文章

  • (十一)java线程Phaser

    摘要:本人邮箱欢迎转载转载请注明网址代码已经全部托管有需要的同学自行下载引言讲完了和今天讲一个跟这两个类有点类似的移相器中引入了一种新的可重复使用的同步屏障称为移相器拥有与和类似的功劳但是这个类提供了更加灵活的应用和都是只适用于固定数量的参与者 本人邮箱: 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kcogithub: https://github....

    eccozhou 评论0 收藏0
  • Java线程进阶(十八)—— J.U.Csynchronizer框架:CountDownLatc

    摘要:线程可以调用的方法进入阻塞,当计数值降到时,所有之前调用阻塞的线程都会释放。注意的初始计数值一旦降到,无法重置。 showImg(https://segmentfault.com/img/remote/1460000016012041); 本文首发于一世流云的专栏:https://segmentfault.com/blog... 一、CountDownLatch简介 CountDow...

    Elle 评论0 收藏0
  • Java线程进阶(十)—— J.U.Csynchronizer框架:CyclicBarrier

    摘要:当到达栅栏后,由于没有满足总数的要求,所以会一直等待,当线程到达后,栅栏才会放行。任务其实就是当最后一个线程到达栅栏时,后续立即要执行的任务。 showImg(https://segmentfault.com/img/remote/1460000016010958); 本文首发于一世流云专栏:https://segmentfault.com/blog... 一、CyclicBarri...

    tulayang 评论0 收藏0
  • (十)java线程CountDownLatch

    摘要:本人邮箱欢迎转载转载请注明网址代码已经全部托管有需要的同学自行下载引言有一个同步助手可以让一个或一些线程等待直到另外一些线程执行完一些操作这就是理论在初始化的时候需要一个参数调用的线程会一直等待直到其他线程调用使清空为通常所有等待中的线程会 本人邮箱: 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kcogithub: https://github...

    陈江龙 评论0 收藏0
  • Java线程编程同步器

    摘要:倒计时锁,线程中调用使进程进入阻塞状态,当达成指定次数后通过继续执行每个线程中剩余的内容。实现分阶段的的功能测试代码拿客网站群三产创建于年月日。 同步器 为每种特定的同步问题提供了解决方案 Semaphore Semaphore【信号标;旗语】,通过计数器控制对共享资源的访问。 测试类: package concurrent; import concurrent.th...

    liangdas 评论0 收藏0

发表评论

0条评论

lingdududu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<