一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Java并发CountDownLatch编程开发示例

时间:2016-08-23 编辑:简简单单 来源:一聚教程网

在多线程编程时,常常需要解决线程同步问题,在上一节讲得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              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执行完成。
全部线程执行完毕

热门栏目