高级JAVA - 多线程之CountDownLatch

package com.xbz.thread.juc.countDownLatch;

import java.util.concurrent.CountDownLatch;

/**
 * @title CountDownLatch类的应用
 *      CountDownLatch类位于java.util.concurrent(简称juc)包下 , 它是一个同步工具类 , 利用它可以实现类似计数器的功能
 *      比如有一个任务A , 它要等待其他4个任务结束执行之后才能执行 , 此时就可以利用CountDownLatch来实现这种功能了
 *      主要用来协调多个线程之间的同步 , 或者说起到线程之间的通信 ( 而不是用作互斥的作用 )
 *      CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后 , 再继续执行 . 使用一个计数器进行实现 .
 *      计数器初始值为线程的数量 .
 *      当每一个线程完成自己任务后 , 计数器的值就会减一(手动) .
 *      当计数器的值为0时 , 表示所有的线程都已经完成了任务 , 然后在CountDownLatch上等待的线程就可以恢复执行任务
 *
 * CountDownLatch类只提供了一个构造器 :
 *      public CountDownLatch(int count)//参数count为计数值 , 即需要先执行几个线程
 *
 * CountDownLatch类的主要方法 :
 *      void await()//调用await()方法的线程会被挂起 , 它会等待直到count值为0才继续执行
 *      boolean await(long timeout, TimeUnit unit)//和await()类似 , 只不过等待一定的时间后count值还没变为0的话也会继续执行
 *      void countDown()//将count值减1

 * CountDownLatch的不足
 *      CountDownLatch是一次性的 , 计数器的值只能在构造方法中初始化一次 , 之后没有任何机制再次对其设置值 , 当CountDownLatch使用完毕后 , 它不能再次被使用
 * @author Xingbz
 * @createDate 2018-7-18
 */
public class MainDemo {
    public static void main(String[] args) throws Exception {
//        demo1();
        demo2();
    }

    /**
     * @title CountDownLatch典型用法1
     * @description 某一线程在开始运行前等待n个线程结束执行 . 
     *      将CountDownLatch的计数器初始化为n new CountDownLatch(n)  , 
     *      每当一个任务线程结束执行 , 就将计数器减1 countdownlatch.countDown() , 
     *      当计数器的值变为0时 , 在CountDownLatch上 await() 的线程就会被唤醒 . 
     *      一个典型应用场景就是启动一个服务时 , 主线程需要等待多个组件加载完毕 , 之后再继续执行 . 
     * @author Xingbz
     */
    public static void demo1() throws Exception {
//        final CountDownLatch latch = new CountDownLatch(2);//2个线程后开始执行
        final CountDownLatch latch = new CountDownLatch(2) {//此处是为了演示线程过程特意重写了await方法, 实际应用如上声明即可
            @Override
            public void await() throws InterruptedException {
                System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入阻塞状态 . 当前计数器 : " + this.getCount());
                super.await();
            }
        };//2个线程后开始执行

        runThread("线程1", 2000L, latch);
        runThread("线程2", 3000L, latch);

        System.out.println("等待2个子线程结束执行...");
        latch.await();//当前线程挂起 , 直到计数器为0
//      latch.await(20, TimeUnit.SECONDS);//最多阻塞20S , 20S后即使计数器不是0也继续往下执行
        System.out.println("2个子线程已经结束执行");
        System.out.println("继续执行主线程");
    }


    /**
     * @title CountDownLatch典型用法2
     * @description
     *      实现多个线程开始执行任务的最大并行性 . 
     *      注意是并行性 , 不是并发 , 强调的是多个线程在某一时刻同时开始执行 . 
     *      类似于赛跑 , 将多个线程放到起点 , 等待发令枪响 , 然后同时开跑 . 
     *      做法是额外初始化一个共享的CountDownLatch(1) , 将其计数器初始化为1 , 
     *      多个线程在开始执行任务前首先 coundownlatch.await() , 当主线程调用 countDown() 时 , 计数器变为0 , 多个线程同时被唤醒
     * @author Xingbz
     */
    public static void demo2() throws Exception {
        CountDownLatch countDown = new CountDownLatch(1);
        CountDownLatch latch = new CountDownLatch(5);

        for (int i = 0; i < 5; ++i) {// 依次创建并启动处于等待状态的5个线程
            runThread("线程" + (i + 1), 2000L, countDown, latch);
        }

        System.out.println("执行了5个线程并进入了阻塞状态 . 等待统一启动 . . . ");
        countDown.countDown();
        System.out.println("主控器结束计数 , 所有线程同时开始执行 . . .");
        latch.await();
        System.out.println("完成");
    }

    /**
     * @title 模拟启动一个子线程工作
     * @param threadName 线程名称
     * @param sleepTime 休眠时间
     * @param countDown 主控制器 CountDownLatch 负责所有线程启动后先进入阻塞 "预备" 状态
     * @param latch 辅控制器 CountDownLatch 负责所有线程执行完成后继续运行主线程
     * @author Xingbz
     */
    private static void runThread(String threadName, Long sleepTime, CountDownLatch countDown, CountDownLatch latch) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("子线程[" + Thread.currentThread().getName() + "]准备执行");
                countDown.await();
                Thread.sleep(sleepTime);
                System.out.println("子线程[" + Thread.currentThread().getName() + "]正在执行");
                Thread.sleep(sleepTime);
                latch.countDown();//子线程结束执行 , latch计数器减1
                System.out.println("子线程[" + Thread.currentThread().getName() + "]结束执行 , latch计数器-1 . 当前计数器 : " + latch.getCount());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, threadName);
        thread.start();
    }

    /**
     * @title 模拟启动一个子线程工作
     * @param threadName 线程名称
     * @param sleepTime 休眠时间
     * @param latch CountDownLatch
     * @author Xingbz
     */
    private static void runThread(String threadName, Long sleepTime, CountDownLatch latch) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("子线程[" + Thread.currentThread().getName() + "]正在执行");
                Thread.sleep(sleepTime);
                latch.countDown();//子线程结束执行 , latch计数器减1
                System.out.println("子线程[" + Thread.currentThread().getName() + "]结束执行 , latch计数器-1 . 当前计数器 : " + latch.getCount());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, threadName);
        thread.start();
    }
}

 

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