多线程之CountDownLatch和CyclicBarrier的区别和用法

一.CountDownLatch的使用

CountDownLatch经常用于监听某些初始化操作,等初始化执行完毕后,再通知主线程继续工作。

CountDownLatch定义:

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行



下面我们来看下CountDownLatch的使用方法:

public class CountDownLatchTest {

	public static void main(String[] args) {
		//实例化一个CountDownLatch对象
		final CountDownLatch countDownLatch = new CountDownLatch(2);
		//创建一个线程t1
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("进入线程t1  等待其他线程处理完成……");
					countDownLatch.await();
					System.out.println("t1线程继续执行……");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"t1").start();
		//创建一个线程t2
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("进入线程t3进行初始化操作……");
					Thread.sleep(2000);
					System.out.println("t3线程执行完毕。。。");
					countDownLatch.countDown();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"t2").start();
		//创建一个线程t3
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("进入线程t2进行初始化操作……");
					Thread.sleep(4000);
					System.out.println("t2线程执行完毕。。。");
					countDownLatch.countDown();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"t3").start();
	}
}

执行结果如下:


进入线程t1  等待其他线程处理完成……
进入线程t3进行初始化操作……
进入线程t2进行初始化操作……
t3线程执行完毕。。。
t2线程执行完毕。。。
t1线程继续执行……

分析CountDownLatch

运行程序我们会发现当我们在t1调用CountDownLatch的await()方法时,就好比我们调用了wait()方法,当前线程会处于阻塞状态,直到等到t2和t3完全执行完毕并且调用countDown()方法时,我们才能唤醒t1继续进行执行,CountDownLatch就好比一个计时器,我们可以让当前线程调用CountDownLatch中的await()方法进行等待,如果想让当前线程继续执行,我们必须让CountDownLatch获得初始化时候传入的构造参数个countDown()方法,我们才能继续执行。

CountDownLatch的使用场景:

在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面的操作。 这个时候就可以使用CountDownLatch。CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。

二.CyclicBarrier的使用

假设有一个场景,每个线程代表一个跑步的运动员,当运动员都准备好之后,才一起出发,只要有一个运动员还没有准备好,所有线程就一起等待。

CyclicBarrier的定义(来自百度):

CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。



下面我们来看CyclicBarrier 的使用小Demo:

public class CyclicBarrireTest {

	public static void main(String[] args) {
		CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		try {
			executorService.execute(new Runner(cyclicBarrier, "张三"));
			executorService.execute(new Runner(cyclicBarrier, "李四"));
			executorService.execute(new Runner(cyclicBarrier, "王五"));
		} finally {
			executorService.shutdown();
		}
	}
	
	static class Runner implements Runnable{
		private String name;
		private CyclicBarrier cyclicBarrier;
		public Runner(CyclicBarrier cyclicBarrier,String name) {
			this.name = name;
			this.cyclicBarrier=cyclicBarrier;
		}
		
		@Override
		public void run() {
			try {
				Thread.sleep(1000*new Random().nextInt(5));
				System.out.println(Thread.currentThread().getName() +"已经准备好");
					cyclicBarrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			System.err.println(Thread.currentThread().getName() +"出发!!");
		}
		
	}
}

执行结果如下:

pool-1-thread-1已经准备好
pool-1-thread-3已经准备好
pool-1-thread-2已经准备好
pool-1-thread-2出发!!
pool-1-thread-1出发!!
pool-1-thread-3出发!!

CyclicBarrier 分析结果:

上述程序我们创建了一个线程池,这个线程池中有三个线程,每个线程都传递了一个相同的
CyclicBarrier 对象和运动员的名字,我们Runner类中的run方法使每一个进来的运动员都休眠0-5秒的时间,然后调用await()方法,就是说每个线程进来都需要进行等待,直到所有的CyclicBarrier 都处于准备好了的状态,所有线程才能统一开始执行!

CyclicBarrier 使用场景

  • CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个Excel保存了用户所有银行流水,每个Sheet保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,再用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水。

  • 三.cyclicBarrier和CountDownLatch的区别

1、CountDownLatch简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。
2、cyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行!
3、CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

4,、CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。如果被中断返回true,否则返回false。




java架构师视频教程,高并发集群分布式视频教程,高并发处理方式,大数据视频教程出售QQ:694042039

    原文作者:我还是个少年
    原文地址: https://blog.csdn.net/a347911/article/details/53465445
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞