CountDownLatch的原理和并行性

这里借鉴了一篇总结得不错的博客:https://blog.csdn.net/joenqc/article/details/76794356

CountDownLatch的概念

CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。

CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在CountDownLatch上等待的线程就可以恢复执行任务。

CountDownLatch的用法

CountDownLatch典型用法1:某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为n new CountDownLatch(n) ,每当一个任务线程执行完毕,就将计数器减1 countdownlatch.countDown(),当计数器的值变为0时,在CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

CountDownLatch典型用法2:实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计数器初始化为1,多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。

这里根据用法二,举个简单的例子:

public class Single {
   
        private Single(){  ...  }
 
        public static Single getInstance() {  ...  }
        return 0;
}
public static void main(String[] args) {
	final CountDownLatch latch = new CountDownLatch(1);    //CountDownLatch传入线程数(1个任务)
	int threadCount = 1000;
	for (int i = 0; i < threadCount; i++) {
		new Thread() {
			@Override
			public void run() {
				try {
					latch.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Single.getInstance().hashCode());
			}
		}.start();
	}
	latch.countDown();
}

CountDownLatch的不足

CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。

使用场景:

1、比如说需要去解析一个excel的数据,为了更快的解析则每个sheet都使用一个线程去进行解析,但是最后的汇总数据的工作则需要等待每个sheet的解析工作完成之后才能进行,不同大小的excel表读取所需的时间长短不一,但是需要等待所有的表都读取完了才可以进行汇总,这时就快的线程就要等待慢的线程完成,这就可以使用CountDownLatch。

参考:https://blog.csdn.net/LightOfMiracle/article/details/73456832

public class CountDownLatchDemo {
	
	public static void main(String[] args) throws InterruptedException{
		
		CountDownLatch countDownLatch = new CountDownLatch(2){
			@Override
			public void await() throws InterruptedException {    //重写await()方法
				super.await();
				System.out.println(Thread.currentThread().getName() +  " count down is ok");
			}
		};
		
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				//do something
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + " is done");
				countDownLatch.countDown();    //表格1读取完成,调用countDown(),正在进行的任务线程-1
			}
		}, "thread1");
		
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				//do something
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + " is done");
				countDownLatch.countDown();    //表格2读取完成,调用countDown(),正在进行的任务线程-1
			}
		}, "thread2");
		
		
		thread1.start();
		thread2.start();
		
		countDownLatch.await();    //在两个线程完成之后,汇总进行之前,需要调用await()
	}
	
}

2、一群人每天也要上班,话说今天定了个饭店,一起吃个饭,通知大家下班去饭店集合。每个人人在不同的地方上班,必须等到所有人人到场才能吃饭。

参考:https://blog.csdn.net/lmj623565791/article/details/26626391

public class CountDownLatchDemo {

private static CountDownLatch countDownLatch = new CountDownLatch(3);    //3个任务,3个线程

public static void main(String[] args)
	{
		new Thread()
		{
			public void run()
			{
				friend1();    //朋友1去饭店所需要的过程
                latch.countDown();    //第一个人到达饭店,任务线程1完成
			};
		}.start();
		new Thread()
		{
			public void run()
			{
				friend2();    //朋友2去饭店所需要的过程
                latch.countDown();    //第二个人到达饭店,任务线程2完成
			};
		}.start();
		new Thread()
		{
			public void run()
			{
				friend3();    //朋友3去饭店所需要的过程
                latch.countDown();    //第三个人到达饭店,任务线程3完成
			};
		}.start();

		latch.await();        //所有的线程完成了,在进行下一步(吃饭)之前需要调用await()
		eatTogether();    //人到齐了,恰饭!
	}
}

猜你喜欢

转载自blog.csdn.net/Nobody_else_/article/details/97623701