Java并发CountDownLatch编程开发示例

在多线程编程时,常常需要解决线程同步问题,在上一节讲得BlockQueue的take和put方法,是通过阻塞来控制生产者和消费者执行流程,它其实也是在解决线程同步问题。另外在线程同步问题还可以通过很多方式解决比如信号量,栅栏,闭锁。今天所说的CountDownLatch就是一种闭锁同步类,它的作用就像一扇门,在达到结束状态之前门是关闭的,并且没有任何线程可以通过这扇门,当达到了结束状态,门打开,所有的线程可以通过。并且结束后,不会再关闭。

CountDownLatch简介

CountDownLatch是一种闭锁的实现,它通过一个计数器来实现。
1、在初始化时,指定等待线程的数量。
2、每个线程结束时,调用countDown将等待数量减1
3、它会有一个await方法,来等待线程全部执行结束。

CountDownLatch用法

/**
CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行
CountDownLatch如其所写,是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。下面以一个模拟运动员比赛的例子加以说明。
*/
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.Executor;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
 
  public class CountDownLatchDemo {
     private static final int PLAYER_AMOUNT = 5;
     public CountDownLatchDemo() {
         // TODO Auto-generated constructor stub   
      }
     /**
      * @param args
      */
     public static void main(String[] args) {
         // TODO Auto-generated method stub
         //对于每位运动员,CountDownLatch减1后即结束比赛
         CountDownLatch begin = new CountDownLatch(1);
         //对于整个比赛,所有运动员结束后才算结束
         CountDownLatch end = new CountDownLatch(PLAYER_AMOUNT);
         Player[] plays = new Player[PLAYER_AMOUNT];
        
         for(int i=0;i<PLAYER_AMOUNT;i++)
             plays[i] = new Player(i+1,begin,end);
        
         //设置特定的线程池,大小为5
         ExecutorService exe = Executors.newFixedThreadPool(PLAYER_AMOUNT);
         for(Player p:plays)
             exe.execute(p);            //分配线程
         System.out.println("Race begins!");
         begin.countDown();
         try{
             end.await();            //等待end状态变为0,即为比赛结束
         }catch (InterruptedException e) {
             // TODO: handle exception
             e.printStackTrace();
         }finally{
             System.out.println("Race ends!");
         }
         exe.shutdown();
     }
 }
 
接下来是Player类

import java.util.concurrent.CountDownLatch;
 
 
 public class Player implements Runnable {
 
     private int id;
     private CountDownLatch begin;
     private CountDownLatch end;
     public Player(int i, CountDownLatch begin, CountDownLatch end) {
         // TODO Auto-generated constructor stub
         super();
         this.id = i;
         this.begin = begin;
         this.end = end;
     }
 
     @Override
     public void run() {
         // TODO Auto-generated method stub
         try{
             begin.await();        //等待begin的状态为0
             Thread.sleep((long)(Math.random()*100));    //随机分配时间,即运动员完成时间
             System.out.println("Play"+id+" arrived.");
         }catch (InterruptedException e) {
             // TODO: handle exception
             e.printStackTrace();
         }finally{
             end.countDown();    //使end状态减1,最终减至0
         }
     }
 }

代码以及使用场景示例

假设如下场景:在一个批处理程序中,一次从一个远程接口中,拉取数据需要耗时非常长的时间,这时考虑到使用多线程去拉,可以减少线程等待的时间。这时就可以使用CountDownLatch来做线程同步,来保证每个线程都执行完成来继续下面的流程。

public class CountDownLatchDemo {

    private static ExecutorService executor= Executors.newFixedThreadPool(5);

    public static void main(String[] args){

        CountDownLatch countDownLatch=new CountDownLatch(10);

        for(int index=0;index<10;index++) {
            executor.execute(new Task(index*10,(index+1)*10,countDownLatch));
        }

        try {
            System.out.println("开始等待线程执行...");
            countDownLatch.await();
            System.out.println("全部线程执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private static class Task implements Runnable{

        private int startIndex;

        private int count;

        private CountDownLatch countDownLatch;

        public Task(int startIndex, int count,CountDownLatch countDownLatch) {
            this.startIndex = startIndex;
            this.count = count;
            this.countDownLatch=countDownLatch;
        }

        @Override
        public void run() {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getId()+"执行完成。");
            countDownLatch.countDown();
        }
    }
}

以上代码模拟了上述假设中得场景。其中使用了线程池来做线程管理,这些对象也将在后续介绍。
输出结果如下:

开始等待线程执行...
12执行完成。
11执行完成。
12执行完成。
11执行完成。
13执行完成。
13执行完成。
9执行完成。
10执行完成。
12执行完成。
11执行完成。
全部线程执行完毕

时间: 2024-09-27 15:55:03

Java并发CountDownLatch编程开发示例的相关文章

java线程并发countdownlatch类使用示例_java

复制代码 代码如下: package com.yao; import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; /** * CountDownLatch是个计数器,它有一个初始数, * 等待这个计数器的线程必须等到计数器倒数到零时才可继续. */public class CountDownLatchTe

Java并发编程示例(十):线程组_java

对线程分组是Java并发API提供的一个有趣功能.我们可以将一组线程看成一个独立单元,并且可以随意操纵线程组中的线程对象.比如,可以控制一组线程来运行同样的任务,无需关心有多少线程还在运行,还可以使用一次中断调用中断所有线程的执行. Java提供了ThreadGroup类来控制一个线程组.一个线程组可以通过线程对象来创建,也可以由其他线程组来创建,生成一个树形结构的线程. 根据<Effective Java>的说明,不再建议使用ThreadGroup.建议使用Executor. --D瓜哥特此

Java并发编程示例(九):本地线程变量的使用_java

共享数据是并发程序最关键的特性之一.对于无论是继承Thread类的对象,还是实现Runnable接口的对象,这都是一个非常周重要的方面. 如果创建了一个实现Runnable接口的类的对象,并使用该对象启动了一系列的线程,则所有这些线程共享相同的属性.换句话说,如果一个线程修改了一个属性,则其余所有线程都会受此改变的影响. 有时,我们更希望能在线程内单独使用,而不和其他使用同一对象启动的线程共享.Java并发接口提供了一种很清晰的机制来满足此需求,该机制称为本地线程变量.该机制的性能也非常可观.

Java并发编程示例(四):可控的线程中断_java

在上一节"线程中断"中,我们讲解了如何中断一个正在执行的线程以及为了中断线程,我们必须对Thread动点什么手脚.一般情况下,我们可以使用上一节介绍的中断机制.但是,如果线程实现了一个分配到多个方法中的复杂算法,或者方法调用中有一个递归调用,我们应该使用更好的方式来控制线程的中断.为此,Java提供了InterruptedException异常.当检测到中断请求时,可以抛出此异常,并且在run()方法中捕获. 在本节,我们将使用一个线程查找指定目录及其子目录下文件来演示通过使用Inte

了解Java 8功能如何让并发性编程变得更容易

在期待已久的 Java 8 版本中,并发性方面已实现了许多改进,其中包括在java.util.concurrent 层级中增加新的类和强大的新并行流 功能.设计流的目的是与lambda 表达式 共同使用,Java 8 的这项增强也使得日常编程的其他很多方面变得更加简便.(参见介绍 Java 8 语言的 指南文章,了解对于 lambda 表达式的介绍及相关 interface改动.) 本文首先介绍了新的 CompletableFuture 类如何更好地协调异步操作.接下来,我将介绍如何使用并行流(

推荐阅读Java并发性领域编程最值得一读的力作《JAVA并发编程实践》

我的第一次之给<JAVA并发编程实践>写推荐序英文书名:Java Concurrency in Practice 中文书名:JAVA并发编程实践 这是一本入围17届Jolt大奖的书,虽然最终他没有获奖,但是这只是与政治有关的.:) 推荐序原文如下: http://book.csdn.net/bookfiles/398/10039814644.shtml 在汗牛充栋的 Java 图书堆中,关于并发性的书籍却相当稀少,然而这本书的出现,将极大地弥补了这一方面的空缺.即使并发性编程还没进入到您的 J

《 Java并发编程从入门到精通》目录和序言

目 录 第一部分:线程并发基础   第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以采取不的一样机制(apache和tomcat?

Java并发编程相关面试问题

基础概念 1.什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)? 原子操作(atomic operation)意为"不可被中断的一个或一系列操作" .处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作. 在Java中可以通过锁和循环CAS的方式来实现原子操作. CAS操作--Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作. 原子操作是

Java并发编程【1.2时代】

    本文介绍了Java原生的多线程技术(1.2),通过详细介绍wait和notify相关的机制.基础的多线程技术以及基于这些技术的等待超时.线程间的通信技术和线程池高阶技术,最后通过一个基于线程池的简单文本web服务器-MollyServer,来阐明多线程带来好处.通过介绍这些技术,展示了在没有使用Java并发包的时代(1.5-)是如何完成Java的多线程编程,为理解Java5提供了良好帮助. 线程简介        Java从诞生开始就明智的选择内置对多线程的支持,这将Java语言同其他同