java并发学习03---CountDownLatch 和 CyclicBarrier

CountDownLatch,顾名思义就是一个倒计时器。(其实Latch的意思是门闩,这个词的本意是不断的计数减一,减到0了就打开门闩放行,但一般咱们仍是叫它倒计时器)java

这个倒计时器和咱们传统意义上的倒计时器并不彻底同样,这个倒计时器的意思是,一开始规定几个线程(好比说咱们这里一开始有10个线程),那么每一个线程结束以后,会调用倒计时器实例对象的方法,让它的“计数器”减一,当计数器减到0时,门闩打开放行。多线程

CountDownLatch的简单原理和应用 :dom

在主线程调用CountDownLatch的实例方法  await()  就可使主线程阻塞起来,经过子线程调用实例方法  countdown()  ,使计数器减1,直到倒计时器减到0以后,门闩打开,主线程能够继续执行。ide

简单来讲就是主线程得等全部的子线程都执行完了以后才能执行,这是一个颇有用的线程。函数

对应生活中的场景,我想到《爱情公寓》中有一集,是胡一菲要帮她老弟展博和宛瑜拍婚纱照,一灰同志就如战场指挥官通常分派它的好友们去处理各类事情,灯光,摄像,摄影棚,头纱,戒指等等,假如把一灰同志当作是主线程,其他的好友当作是多个子线程的话,那么一灰同志就得等他们全部的线程都执行完任务以后才能振臂一呼:“开拍”。(虽然最后基本上都没搞定。。)this

ps:什么,你不知道一灰同志是谁?----- 胡一灰啦spa

什么,你说拿情景喜剧里面的片断来举例子简直是扯淡,好吧,那就再举一个实际点的例子,好比说我如今在写的一个多线程的小例子,使用多线程下载图片(不必定是图片,也能够是其余的资源),思路很简单,就是先获取目标文件的长度,分红几块,每一个线程下一块,都下载好了以后就合并。线程

那么这里面有一个关键的问题就是如何才能让主线程等待全部的子线程都下载好了以后再进行合并,CountDownLatch则正好对应了这样的场景。翻译

实例代码嘛,仍是直接用的书上的例子吧(是一个发射火箭的例子,线程池中的每个线程表明火箭发射前的一项技术检查,可是做者偷懒,就用一个for循环代替了,嘻嘻,我也偷下懒):code

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

/**
 * 演示倒计时器的简单使用
 */
public class Lesson20_CountDownLatch implements Runnable{
    static final CountDownLatch end = new CountDownLatch(10);
    static final Lesson20_CountDownLatch demo = new Lesson20_CountDownLatch();

    @Override
    public void run() {
        try {
            //模拟检查任务
            Thread.sleep(new Random().nextInt(10)*1000);
            System.out.println("check complete");
            //每完成一个线程,计数器就减一
            end.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10 ; i++) {
            exec.submit(demo);
        }
        //阻塞主线程,等待检查
        try {
            end.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //发射火箭
        System.out.println("Fire!");
        exec.shutdown();
    }
}

据说倒计时器不仅仅能够阻塞一个线程,还能够阻塞一组线程,你们能够试试。 

 

 

CyclicBarrier (翻译过来好像是循环栅栏)

CyclicBarrier和CountDownLatch的做用很类似,但我的感受仍是有所区别的。CyclicBarrier也实现了线程间的相互等待,可是并无阻塞主线程。

仍是上面那个拍婚纱的例子,在上面那个例子中,一灰同志要等全部人都把任务完成才能帮他老弟和宛瑜拍婚纱,其实能够有两种办法实现:

a.   一灰知道她一共分派了5个任务,每有一我的来报道,她就知道解决了一个,还剩4个任务,当全部人都一 一回来以后,她就知道任务都搞定了,这是倒计时器的作法,请注意这个时候主线程是被阻塞住的,也就是说一灰这时啥也不能干,眨下眼睛都不行。你能够认为她睡着了。

b.  一灰跟他们全部人说,大家所有都搞定了再一块儿回来,不然(此时只见一灰单手将不锈钢勺子给掰弯了),你们见状顿时四散奔逃。。

    因而当悠悠和关谷搞定了摄影师以后便赶快打电话给子乔,“子乔,你头纱搞到没”,“早就搞好了,我把我前女朋友叫过来了,她今天结婚”(只见一抹鲜红的巴掌印留在了子乔的脸上),咳咳。。 好的,就这样他们一群人相互联系,确认全部人都搞定了本身的任务,这才回去面见陛下,哦不,是咱们的指挥官一灰同志。

这里注意,此时,一灰是能够干任何事情的,该吃手抓饼吃手抓饼,该喝茶喝茶,时不时还能打个电话催一催进度。

 

那么,问题来咱们这里是用一灰来模拟的主线程,那么他们还没回来,一灰就开始带着他老弟去拍婚纱了怎么办。

因此说,拍婚纱这个行为不是由主线程控制的。咱们能够看一下CyclicBarrier的构造函数,里面有这么一条:

 CyclicBarrier(int parties, Runnable barrierAction)

这里后面这个barrierAction就对应了拍婚纱这个行为。

那循环栅栏中的循环是什么意思呢?

这就是它和倒计时器的另一个区别了,倒计时器结束了,门闩打开了就关不上了,而循环栅栏则能够重复使用,好比一灰让他们先去买10台豆浆机,再去买10台面包机,前后顺序不能反,因而他们会相互联系确认都买了豆浆机,而后吃个饭庆祝下,哦不是,而后打电话给一灰汇报下,而后再各自去买面包机,再相互联系,是否每一个人都买了,确认以后,再吃个饭庆祝一下。。(注意,这里的打电话,吃饭庆祝等行为都是模拟的barrierAction)

  • CyclicBarrier的简单原理和应用:

      CyclicBarrier与CountDownLatch不一样,并非主线程拿着一个计时器,而是每一个子线程都持有一个CyclicBarrier的实例,若是有5个线程持有了相同的CyclicBarrier的实例对象,那么这5个线程就都得确保其余线程搞定了,我才能继续执行,上面解释过,这里就不解释了

 偷懒如我,仍是用的书上的例子(什么,让我实现本身举的爱情公寓的例子,什么,你说啥,我这信号很差,听不清了,拜拜):

package thread.thread_util;

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

/**
 * 循环栅栏
 */
public class Lesson19_CyclicBarrier {
    public static class Soldier implements Runnable {
        private String soldier;
        private final CyclicBarrier cyclic;

        Soldier(CyclicBarrier cyclic,String soldierName) {
            this.cyclic = cyclic;
            this.soldier = soldierName;
        }

        @Override
        public void run() {
            //这里为了展现栅栏是能够重复使用的,因此使用了2次await
            try {
                //等到全部士兵到齐
                cyclic.await();

                dowork();
                //等待全部士兵完成工做
                cyclic.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }

        void dowork() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("soldier" + ": 任务完成");
        }
    }

    //线程完毕时执行的动做
    public static class BarrierRun implements Runnable {
        boolean flag;
        int N;
        public BarrierRun(boolean flagk, int N) {
            this.flag = flag;
            this.N = N;
        }

        @Override
        public void run () {
            if(flag) {
                System.out.println(String.format("司令:【士兵%d个,任务完成】",N));
            } else {
                System.out.println(String.format("司令:【士兵%d个,集合完毕】",N));
                flag = true;
            }
        }
    }
    public static void main(String args[]) throws InterruptedException{
        final int N = 5;
        boolean flag = false;
        CyclicBarrier cyclic = new CyclicBarrier(N,new BarrierRun(flag,N));

        System.out.println("集合队伍");
        for (int i = 0; i < 5 ; i++) {
            System.out.println("士兵" + i + "报道");
            new Thread(new Soldier(cyclic,"士兵" + (i + 1))).start();
        }
//        System.out.println("主线程over");   你能够试试,这里主线程不会被阻塞的
    }
}

 

关于这两个同步器的使用就告一段落了,如有错漏之处,请在评论区指出。

相关文章
相关标签/搜索