线程通信——CountDownLatch和CyclicBarrier

来源:互联网 发布:dyndns免费域名申请 编辑:程序博客网 时间:2024/04/28 19:42

CountDownLatch和CyclicBarrier

CyclicBarrier和CountDownLatch都是java的线程通信工具类,他们的都是对常用的线程通信模型进行了封装,为了更方便让我们完成线程交互通信。那么这两个通信模型的如何工作,他们俩有什么区别呢。

CyclicBarrier

CyclicBarrier 定义

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other.

主要就是说,CyclicBarrier可以模拟一堆线程相互等待,直到到达一个相同的界值(barrier point),然后一起继续执行。CyclicBarrier的主要作用是模拟线程间相互等待的一个过程。

这是网上找的一个例子,我非常喜欢这个例子

题:
4个玩家一起闯关,只有当4个玩家一起过关时,才能继续下一关

下面是代码

/** * 玩家 */public class CBPlayer implements Runnable {    private CyclicBarrier cyclicBarrier;    private int id;    public CBPlayer(int id, CyclicBarrier cyclicBarrier) {        this.cyclicBarrier = cyclicBarrier;        this.id = id;    }    @Override    public void run() {        try {            System.out.println("玩家" + id + "正在玩第一关...");            cyclicBarrier.await();            System.out.println("玩家" + id + "进入第二关...");        } catch (InterruptedException e) {            e.printStackTrace();        } catch (BrokenBarrierException e) {            e.printStackTrace();        }    }}

Game启动

public class CBGameBarrier {    public static void main(String[] args) {        CyclicBarrier cyclicBarrier = null;        cyclicBarrier = new CyclicBarrier(4, new Runnable() {            @Override            public void run() {                System.out.println("所有玩家进入第二关!2秒后一起进入下一关...");                try {                    TimeUnit.SECONDS.sleep(2);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        });        for (int i = 0; i < 4; i++) {            new Thread(new CBPlayer(i, cyclicBarrier)).start();        }    }}

输出结果

玩家0正在玩第一关...玩家3正在玩第一关...玩家2正在玩第一关...玩家1正在玩第一关...所有玩家进入第二关!2秒后一起进入下一关...玩家0进入第二关...玩家2进入第二关...玩家1进入第二关...玩家3进入第二关...

可以看出4位玩家这里有个相互等待的一个过程,直到4个玩家一起通关后,显示“所有玩家进入第二关!2秒后一起进入下一关…”,这里因为在主线程做了sleep操作,4个玩家还被主线程卡了2秒后才一起进行下一关。
这个例子说明CyclicBarrier可以模拟出线程间相互等待的过程,并且在条件满足时,执行Runnable的run方法后,等待继续一起执行后续任务。

CountDownLatch

CountDownLatch 定义

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

大致就是说,允许一个或者多个线程进行等待,直到countDownlancher条件满足为止,这些线程继续工作。

老实说,我不觉得官方的这段话能让人很清晰的理解清楚这货的用法。

A {@code CountDownLatch} is initialized with a given count.The {@link #await await} methods block until the current count reaches
zero due to invocations of the {@link #countDown} method, after which
all waiting threads are released and any subsequent invocations of {@link #await await} return immediately. This is a one-shot phenomenon – the count cannot be reset. If you need a version that resets the count, consider using a {@link CyclicBarrier}.

这后面又说了countDownlancher是不能重置的,如果你想用重置操作,你可以用CyclicBarrier。

既然这样,那要你何用?不管怎么样,我们把上面的题目再实现一遍。

题:
4个玩家一起闯关,只有当4个玩家一起过关时,才能继续下一关

玩家

public class CDLPlayer  implements Runnable {    private CountDownLatch cdl;    private int id;    public CDLPlayer(int id, CountDownLatch cdl) {        this.cdl = cdl;        this.id = id;    }    @Override    public void run() {        try {            System.out.println("玩家" + id + "正在玩第一关...");            cdl.countDown();//countDown计数+1            cdl.await();            System.out.println("玩家" + id + "进入第二关...");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

GAME 启动

public class CDLGameBarrier {    public static void main(String[] args) {        CountDownLatch cdl = new CountDownLatch(4);        for (int i = 0; i < 4; i++) {            new Thread(new CDLPlayer(i, cdl)).start();        }    }}

输出

玩家1正在玩第一关...玩家2正在玩第一关...玩家3正在玩第一关...玩家0正在玩第一关...玩家0进入第二关...玩家1进入第二关...玩家2进入第二关...玩家3进入第二关...

这里可以发现,没有了中间等待2秒过关的过程,因为我细想了下,如果只用一个CountDownLatch,我不能实现这个功能。但是可以从例子中发现,调用await方法的线程会进行阻塞,调用countdown方法进行计数。CountDownLatch把计数和阻塞的方法分开了。这点区别还是很大的,也就是说,countdownlatch是更灵活的。

于是我想到了下面这个例子
题:
饺子工厂开工,要做600个饺子,有管理者、工人A、工人B、工人C 4个人,管理者负责监管工人的工作进度,每做完200个就向上汇报。

管理者\监工

/** * 监工  * */public class DumplingSupervisor extends Thread{    private CountDownLatch cdl;    private int totalnum = 0;    public DumplingSupervisor(){        cdl = new CountDownLatch(100);    }    public void report(){        synchronized (this) {            cdl.countDown();        }    }    @Override    public void run() {        try {            cdl.await();        } catch (InterruptedException e) {        }        totalnum = 100;        System.out.println("管理者,100个饺子做完了");        synchronized (this) {            cdl = new CountDownLatch(200 - totalnum);        }        totalnum = 200;        try {            cdl.await();        } catch (InterruptedException e) {        }        System.out.println("管理者,200个饺子做完了");        synchronized (this) {            cdl = new CountDownLatch(400  - totalnum);        }        totalnum = 400;        try {            cdl.await();        } catch (InterruptedException e) {        }        System.out.println("管理者,400个饺子做完了");        synchronized (this) {            cdl = new CountDownLatch(600 - totalnum);        }        totalnum = 600;        try {            cdl.await();        } catch (InterruptedException e) {        }        System.out.println("管理者,600个饺子做完了");    }}

包饺子的人\工人

/** * 包饺子的人 * */public class DumplingWork extends Thread {    private DumplingSupervisor supervisor;    private String name;    private int rate;// 饺子/秒, 工作效率。如果是5,就是5个饺子每秒    public DumplingWork(String name, int rate, DumplingSupervisor supervisor) {        this.supervisor = supervisor;        this.name = name;        this.rate = rate;    }    @Override    public void run() {        int count = 200;        for (int i = 0; i < count; i++) {            // 做了一个饺子            try {                Thread.sleep(1000 / rate);            } catch (InterruptedException e) {                e.printStackTrace();            }            supervisor.report();// 每做完一个向领导报告        }        System.out.println(name + "报告,工作做完了");    }}

作坊

/** * 作坊 */public class DumplingWorkShop {    public static void main(String[] args) {        DumplingSupervisor supervisor = new DumplingSupervisor();//监管人        DumplingWork workA = new DumplingWork("A", 20, supervisor);        DumplingWork workB = new DumplingWork("B", 30, supervisor);        DumplingWork workC = new DumplingWork("C", 25, supervisor);        supervisor.start();        workA.start();        workB.start();        workC.start();    }}

输出

管理者,100个饺子做完了管理者,200个饺子做完了管理者,400个饺子做完了B报告,工作做完了C报告,工作做完了管理者,600个饺子做完了A报告,工作做完了

因为A的工作效率最低,A是最后一个完成的,并且在A完成的同时,管理者报告600个饺子的项目完成了。

例子中,ABC工人的线程并没有线程等待,只是进行了单纯的计数任务(report),真正在线程等待的只有管理者。而管理者监控任务时,都去new了新的CountDownLatch

这个例子可以看出CountDownLatch和CyclicBarrier还是有很明显区别的。

总结

老实说,我这人不太会总结,我相信看完例子的同学肯定能了解他们的作用。
CyclicBarrier 可以模拟一堆线程相互等待,直到到达一个相同的界值,然后一起继续执行。
CountDownLatch 主要是用于出一个计数器,等待计数器到达指定目标时,使得等待线程继续执行。

0 0
原创粉丝点击