线程的并发工具类之CountDownLatch和CyclicBarrier原理和使用(七)

今天分析 CountDownLatch和CyclicBarrier原理和使用:
1、CountDownLatch  闭锁
CountDownLatch 这个类能够使一个线程等待其他线程完成各自的工 作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动 所有的框架服务之后再执行。 CountDownLatch 是通过一个计数器来实现的,计数器的初始值为初始任务 的数量。每当完成了一个任务后,计数器的值就会减 1 (CountDownLatch.countDown() 方法)。当计数器值到达 0 时,它表示所有的已 经完成了任务,然后在闭锁上等待 CountDownLatch.await() 方法的线程就可以恢 复执行任务。
应用场景:
实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。 例如,我们想测试一个单例类。如果我们创建一个初始计数为 1 的 CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成 测试。我们只需调用 一次 countDown() 方法就可以让所有的等待线程同时恢复执 行。开始执行前等待 n 个线程完成各自任务:例如应用程序启动类要确保在处理 用户请求前,所有 N 个外部系统已经启动和运行了,例如处理 excel 中多个表单。
 
参见代码如  

import java.util.concurrent.CountDownLatch;

/**
 *类说明:演示CountDownLatch用法,
 * 共5个初始化子线程,6个闭锁扣除点,扣除完毕后,主线程和业务线程才能继续执行
 */
public class UseCountDownLatch {
	
    static CountDownLatch latch = new CountDownLatch(6);

    /*初始化线程*/
    private static class InitThread implements Runnable{

        public void run() {
        	System.out.println("Thread_"+Thread.currentThread().getId()
        			+" ready init work......");
            latch.countDown();
            for(int i =0;i<2;i++) {
            	System.out.println("Thread_"+Thread.currentThread().getId()
            			+" ........continue do its work");
            }
        }
    }

    /*业务线程等待latch的计数器为0完成*/
    private static class BusiThread implements Runnable{

        public void run() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i =0;i<3;i++) {
            	System.out.println("BusiThread_"+Thread.currentThread().getId()
            			+" do business-----");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            public void run() {
            	SleepTools.ms(1);
                System.out.println("Thread_"+Thread.currentThread().getId()
            			+" ready init work step 1st......");
                latch.countDown();
                System.out.println("begin step 2nd.......");
                SleepTools.ms(1);
                System.out.println("Thread_"+Thread.currentThread().getId()
            			+" ready init work step 2nd......");
                latch.countDown();
            }
        }).start();
        new Thread(new BusiThread()).start();
        for(int i=0;i<=3;i++){
            Thread thread = new Thread(new InitThread());
            thread.start();
        }

        latch.await();
        System.out.println("Main do ites work........");
    }
}

执行结果:

2、CyclicBarrier 

CyclicBarrier 的字面意思是可循环使用( Cyclic )的屏障( Barrier )。它要做 的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一 个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。 CyclicBarrier 默认的构造方法是 CyclicBarrier int parties ),其参数表示屏障拦截 的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 我已经到达了屏障,然 后当前线程被阻塞。 CyclicBarrier 还提供一个更高级的构造函数 CyclicBarrie (r int parties,Runnable barrierAction),用于在线程到达屏障时,优先执行 barrierAction ,方便处理更复 杂的业务场景。CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的场景。

 参见代码如 


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;

/**
 *类说明:演示CyclicBarrier用法,共4个子线程,他们全部完成工作后,交出自己结果,
 *再被统一释放去做自己的事情,而交出的结果被另外的线程拿来拼接字符串
 */
public class UseCyclicBarrier {

    private static CyclicBarrier barrier
            = new CyclicBarrier(4,new CollectThread());

    //存放子线程工作结果的容器
    private static ConcurrentHashMap<String,Long> resultMap
            = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        for(int i=0;i<4;i++){
            Thread thread = new Thread(new SubThread());
            thread.start();
        }

    }

    /*汇总的任务*/
    private static class CollectThread implements Runnable{

        @Override
        public void run() {
            StringBuilder result = new StringBuilder();
            for(Map.Entry<String,Long> workResult:resultMap.entrySet()){
            	result.append("["+workResult.getValue()+"]");
            }
            System.out.println(" the result = "+ result);
            System.out.println("do other business........");
        }
    }

    /*相互等待的子线程*/
    private static class SubThread implements Runnable{

        @Override
        public void run() {
        	long id = Thread.currentThread().getId();
            resultMap.put(Thread.currentThread().getId()+"",id);
            try {
                	Thread.sleep(1000+id);
                	System.out.println("Thread_"+id+" ....do something ");
                barrier.await();
            	Thread.sleep(1000+id);
                System.out.println("Thread_"+id+" ....do its business ");
                barrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

结果:

 

3、CountDownLatch CyclicBarrier 辨析
CountDownLatch 的计数器只能使用一次,而 CyclicBarrier 的计数器可以反复 使用。CountDownLatch.await 一般阻塞工作线程,所有的进行预备工作的线程执行countDown,而 CyclicBarrier 通过工作线程调用 await 从而自行阻塞,直到所有工 作线程达到指定屏障,再大家一起往下走。 在控制多个线程同时运行上,CountDownLatch 可以不限线程数量,而 CyclicBarrier 是固定线程数。 同时,CyclicBarrier 还可以提供一个 barrierAction ,合并多线程计算结果。
到此两种工具类分析完成。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值