Java并发之CountDownLatch

简介: Java并发之CountDownLatch

CountDownLatch

CountDownLatch是java多线程包concurrent里的一个常见工具类,通过使用它可以借助线程能力极大提升处理响应速度,且实现方式非常优雅。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

核心api

CountDownLatch(int count) //实例化一个倒计数器,count指定计数个数
countDown() // 计数减一
await() //等待,当计数减到0时,所有线程并行执行

案例

在一个程序中需要调用两个服务的接口后才能执行下一步,那么我们需要判断这连个接口是否调用成功,并且这两个接口需要较长的时间,那么我们就可以使CountDownLatch来完成

抽象类

/**
 * @Description: 抽象类,统一
 * @author BushRo
 * @date 2019-07-16
 * @version 1.0
 *
 */
public abstract class Center implements Runnable {
  private CountDownLatch countDown;   // 计数器
  private String server;          // 服务名称
  private boolean ok;           // 服务是否调用成功标识
  public Center(CountDownLatch countDown, String server) {
    this.countDown = countDown;
    this.server = server;
    this.ok = false;
  }
  @Override
  public void run() {
    try {
      work();
      ok = true;
    } catch (Exception e) {
      e.printStackTrace();
      ok = false;
    } finally {
      if (countDown != null) {
        countDown.countDown();
      }
    }
  }
  /**
   * @Description: 抽象方法
   * @author BushRo
   * @date 2019-07-16
   * @version 1.0
   *
   */
  public abstract void work();
  public CountDownLatch getCountDown() {
    return countDown;
  }
  public void setCountDown(CountDownLatch countDown) {
    this.countDown = countDown;
  }
  public String getServer() {
    return server;
  }
  public void setServer(String server) {
    this.server = server;
  }
  public boolean isOk() {
    return ok;
  }
  public void setOk(boolean ok) {
    this.ok = ok;
  }
}

服务A

/**
 * @Description: 服务A工作
 * @author BushRo
 * @date 2019-07-16
 * @version 1.0
 *
 */
public class ServerA extends Center {
  public ServerA(CountDownLatch countDown) {
    super(countDown, "服务A");
  }
  @Override
  public void work() {
    System.out.println("正在获取 [" + this.getServer() + "]接口数据...");
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("获取 [" + this.getServer() + "] 数据完毕");
  }
}

服务B

/**
 * @Description: 服务B工作
 * @author BushRo
 * @date 2019-07-16
 * @version 1.0
 *
 */
public class ServerB extends Center {
  public ServerB(CountDownLatch countDown) {
    super(countDown, "服务B");
  }
  @Override
  public void work() {
    System.out.println("正在获取 [" + this.getServer() + "]接口数据...");
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("获取 [" + this.getServer() + "] 数据完毕");
  }
}

调用

在使用countDown.await();方法的时候好设置一下时间,否则有一个线程阻塞了,就会导致程序阻塞无法返回值了

public class CheckStartUp {
  private static List<Center> serverList;
  private static CountDownLatch countDown;
  public CheckStartUp() {
  }
  public static boolean checkAllStations() throws Exception {
    // 初始化2个服务
    countDown = new CountDownLatch(2);
    // 把所有站点添加进list
    serverList = new ArrayList<>();
    serverList.add(new ServerA(countDown));
    serverList.add(new ServerB(countDown));
    // 使用线程池
    Executor executor = Executors.newFixedThreadPool(serverList.size());
    for (Center center : serverList) {
      //分别执行三个线程
      executor.execute(center);
    }
    // 等待线程执行完毕
    //countDown.await();
    /**
     * 在等待的时候最好设置一下时间,否则有一个线程阻塞了,
     * 就会导致程序阻塞无法返回值了
     */
    countDown.await(20,TimeUnit.SECONDS);
    for (Center center : serverList) {
      if (!center.isOk()) {
        return false;
      }
    }
    return true;
  }
  public static void main(String[] args) throws Exception {
    long starttime = System.currentTimeMillis();
    boolean result = CheckStartUp.checkAllStations();
    System.out.println("服务的调用结果为:" + result);
    long endtime = System.currentTimeMillis();
    System.out.println("用时:"
        + (endtime - starttime) + "毫秒!");
  }
}

运行效果,这样我们就极大的节约了时间

20190716223821860.png

相关文章
|
3月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
78 2
|
13天前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
2月前
|
算法 Java
JAVA并发编程系列(8)CountDownLatch核心原理
面试中的编程题目“模拟拼团”,我们通过使用CountDownLatch来实现多线程条件下的拼团逻辑。此外,深入解析了CountDownLatch的核心原理及其内部实现机制,特别是`await()`方法的具体工作流程。通过详细分析源码与内部结构,帮助读者更好地理解并发编程的关键概念。
|
16天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
21天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
43 2
|
1月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
29 1
|
2月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
3月前
|
存储 Java
Java 中 ConcurrentHashMap 的并发级别
【8月更文挑战第22天】
52 5
|
3月前
|
存储 算法 Java
Java 中的同步集合和并发集合
【8月更文挑战第22天】
44 5
|
3月前
|
缓存 Java 调度
【Java 并发秘籍】线程池大作战:揭秘 JDK 中的线程池家族!
【8月更文挑战第24天】Java的并发库提供多种线程池以应对不同的多线程编程需求。本文通过实例介绍了四种主要线程池:固定大小线程池、可缓存线程池、单一线程线程池及定时任务线程池。固定大小线程池通过预设线程数管理任务队列;可缓存线程池能根据需要动态调整线程数量;单一线程线程池确保任务顺序执行;定时任务线程池支持周期性或延时任务调度。了解并正确选用这些线程池有助于提高程序效率和资源利用率。
52 2
下一篇
无影云桌面