文章
问答
冒泡
多线程之CountDownLatch

CountDownLatch就是一个同步的工具类,用来协调多个线程之间的同步或者是线程之间的通信。CountDownLatch有一个正数计数器,countDown()方法对计数器做减操作,await()方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。

闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:

  1. 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
  2. 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
  3. 等待直到某个操作所有参与者都准备就绪再继续执行。

CountDownLatch两种典型的用法:

  1. 某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownLatch.countDown(),当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
  2. 实现多个线程任务的最大并行性,注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的 CountDownLatch 对象,将其计数器初始化为 1 :new CountDownLatch(1),多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。

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

代码实例:

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class CountDownLatchDemo implements Callable<Integer> {
    static int threadCount = 15;
    static int awaitMillisecond = 3000;
    static CountDownLatch countDownLatch = new CountDownLatch(threadCount);
    int id;
    public CountDownLatchDemo(int id) {
        this.id = id;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        boolean fireFlag = true;
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(threadCount);
        List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();
        for(int i = 0; i< threadCount; i++) {
            Future<Integer> future = newFixedThreadPool.submit(new CountDownLatchDemo(i));
            futureList.add(future);
        }
        for(int i = 0; i< threadCount; i++) {
            Future<Integer> future = futureList.get(i);
            if(0 == future.get()) {
                fireFlag = false;
                break;
            }
        }
        if(true == fireFlag) {
            System.out.println(getCurrentTime() + "go.");
        }else {
            System.out.println(getCurrentTime() + "countDownLatch.await().");
            //设置超时时间awaitMillisecond
            countDownLatch.await(awaitMillisecond, TimeUnit.MILLISECONDS);
            System.out.println(getCurrentTime() + Thread.currentThread().getName() + ", 线程阻塞");
            //线程阻塞
            countDownLatch.await();
            System.out.println(getCurrentTime() + "thread contains error, do not go.");
        }
        newFixedThreadPool.shutdown();
    }

    @Override
    public Integer call() {
        try {
            if(id < 9) {
                System.out.println(getCurrentTime() + Thread.currentThread().getName()+", 检查完毕.");
                countDownLatch.countDown();
                return 1;
            }else {
                //int a = 1/0;
                System.out.println(getCurrentTime() + Thread.currentThread().getName()+", 存在异常,检查中...");
                Thread.sleep(awaitMillisecond);
                System.out.println(getCurrentTime() + Thread.currentThread().getName()+", 检查失败.");
                return 0;
            }
        }catch (Exception e) {
            System.out.println("call exception.");
            return 0;
        }
    }

    private static String getCurrentTime() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String format = sdf.format(date);
        String ret = "[" + format + "]  ";
        return ret;

    }
}

 

输出结果如下:

 

参考:https://blog.csdn.net/guorui_java/article/details/113827966


关于作者

Taurus
获得点赞
文章被阅读