高并发(8)- 线程并发工具类-CountDownLatch

高并发(8)- 线程并发工具类-CountDownLatch

前言

    上篇文章讲解了线程的并发工具类之ForkJoin,本文就来讲解CountDownLatch并发工具类
    ForkJoin的核心思想是分而治之
    CountDownLatch的则是一组线程等待其他的线程完成工作以后在执行。

什么是CountDownLatch

CountDownLatch是一组线程等待其他线程工作完成以后在执行。例如:一个框架在启动的时候,需要加载各种功能,只有当这些功能加载完成之后,才可以运行主线程,这个时候就可以用到CountDownLatch了,CountDownLatch通过计数器来实现,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了

CountDownLatch执行流程
如图所示,我们定义了一个计数器cnt = 5,TW1和TW2两个线程执行了await()方法,就需要等待计数器减少为0.

  • 所以Td执行了一个操作,CountDown了一次,cnt-1=4
  • Tb线程也操作了一次,CountDown了一次,cnt-1=3
  • Td线程又操作了一次,CountDown了一次,cnt-1=2
  • Ta线程也操作了一次,CountDown了一次,cnt-1=1
  • Tc线程也操作了一次,CountDown了一次,cnt-1=0
    因为这时候cnt计数器=0所以,TW1和TW2两个线程也便推出了等待,继续运行

注意

  1. 一个CountDownLatch可以被多个线程等待,而不是只能被一个线程使用,如图所示TW1和TW2用的同一个CountDownLatch。
  2. 一个线程也可以多次CountDown操作,如图所示,Td线程进行了两次CountDown操作。
  3. 一个线程在CountDown之后还是可以继续运行的,不会在CountDown之后停止,如图所示,Ta和Td两个线程在CountDown之后继续运行。

CountDownLatch实现

  • countDownLatch类中只提供了一个构造器:
//参数count为计数值
public CountDownLatch(int count) {}; 
  • countDownLatch类中还有三个方法比较重要
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException { };   
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
//将count值减1
public void countDown() { };  
  • 具体代码实现
/**
 * @version 1.0
 * @Description CountDownLatchDemo
 * @Author wb.yang
 */
public class CountDownLatchDemo {

	/**
	 * 创建一个计数器为6的CountDownLatch
	 */
	static CountDownLatch latch = new CountDownLatch(6);


	/**
	 * 初始化线程(只有一步,有4个)
	 */
	private static class InitThread implements Runnable {

		@Override
		public void run() {
			System.out.println("Thread_" + Thread.currentThread().getId()
					+ " 准备初始化工作......");
			//初始化线程完成工作了,countDown方法只扣减一次;
			latch.countDown();
			System.out.println("Thread_" + Thread.currentThread().getId()
					+ " .继续做剩余的工作");
		}
	}

	/**
	 * 业务线程
	 */
	private static class BusinessThread implements Runnable {

		@Override
		public void run() {
			try {
				// 等待
				latch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("BusiThread_" + Thread.currentThread().getId()
					+ "运行中-----");
		}
	}


	public static void main(String[] args) throws InterruptedException {
		//单独的初始化线程,初始化分为2步,需要扣减两次
		new Thread(new Runnable() {
			@Override
			public void run() {
				SleepTools.ms(1);
				System.out.println("Thread_" + Thread.currentThread().getId()
						+ "准备初始化工作第一步......");
				latch.countDown();//每完成一步初始化工作,扣减一次
				System.out.println("开始第二步工作.......");
				SleepTools.ms(1);
				System.out.println("Thread_" + Thread.currentThread().getId()
						+ "准备初始化工作第二步......");
				latch.countDown();//每完成一步初始化工作,扣减一次
			}
		}).start();
		//开始主要业务线程
		new Thread(new BusinessThread()).start();
		for (int i = 0; i <= 3; i++) {
			//执行三次初始化工作
			Thread thread = new Thread(new InitThread());
			thread.start();
		}

		latch.await();
		System.out.println("main方法工作........");
	}
}

代码中可以看到,我们定义了一个CountDownLatch,计数器为6,然后以一个业务线程,需要等待CountDownLatch执行完之后才能执行。还有六个初始化线程。

  1. 运行一个初始化线程
  2. 运行业务线程
  3. 进行剩下的四初始化线程
  4. 主线程等待await,等待CountDownLatch

再看下运行结果
CountDownLatch运行结果
我们可以看到,先是初始化线程运行,进行了两次CountDown,证明一个线程可以多次CountDown,然后是剩下四个初始化线程进行CountDown操作,并且在CountDown操作后技术执行代码,然后是主业务线程在CountDown为0的时候执行,由于主线程也是await,所以也需要CountDown为0才可以执行,也证明了一个CountDownLatch可以被多个线程使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章