课外作业之CountDownLatch应用详解


CountDownLatch应用详解

姓名:李纯

学号:150341312

1)概述

   CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

主要方法:
 public CountDownLatch(int count);

构造一个用给定计数初始化的 CountDownLatch。

参数: 

count - 在线程能通过 await() 之前,必须调用countDown() 的次数。

抛出: 

IllegalArgumentException - 如果 count 小于零。


 public void countDown();

递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

如果当前计数大于零,则将计数减少 1。

如果新的计数为零,出于线程调度目的,将重新启用所有的等待线程。

如果当前计数等于零,则不发生任何操作。


 public void await(long timeout,TimeUnitunit) throws InterruptedException

使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

如果当前计数为零,则此方法立刻返回 true 值。

如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直处于休眠状态:

· 由于调用 countDown() 方法,计数到达零;或者

· 其他某个线程中断当前线程;或者

· 已超出指定的等待时间。

如果计数到达零,则该方法返回 true 值。

如果当前线程:

· 在进入此方法时已经设置了该线程的中断状态;或者

· 在等待时被中断

则抛出 InterruptedException,并且清除当前线程的已中断状态。如果超出了指定的等待时间,则返回值为 false。如果该时间小于或等于零,则此方法根本不会等待。

参数: 

timeout - 要等待的最长时间

unit - timeout 参数的时间单位。

返回: 

如果计数到达零,则返回 true;如果在计数到达零之前超过了等待时间,则返回 false。

抛出: 

InterruptedException - 如果当前线程在等待时被中断。

 

2)应用示例:

   

示例1:

将两位工人协作工作的开始时间和结束时间显示在屏幕上

   package lichun;

 

import java.sql.Date;

import java.text.SimpleDateFormat;

import java.util.concurrent.CountDownLatch;

 

public class CoutDownLatchDemo {

 final static SimpleDateFormatsdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  

    @SuppressWarnings("deprecation")

public static void main(String[]args)throws InterruptedException {  

        CountDownLatch latch=new CountDownLatch(2);//两个工人的协作  

        Worker worker1=new Worker("zhang san", 5000,latch);  

        Worker worker2=new Worker("li chun", 8000,latch);  

        worker1.start();//  

        worker2.start();//  c

        latch.await();//等待所有工人完成工作  

        System.out.println("all work done at "+sdf.format(new Date(2017, 11, 14)));

}

    static class Workerextends Thread{  

        String workerName;   

        int workTime;  

        CountDownLatch latch;  

        public Worker(StringworkerName ,int workTime ,CountDownLatchlatch){  

             this.workerName=workerName;  

             this.workTime=workTime;  

             this.latch=latch;  

        }  

        public void run(){  

            System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date(2017, 11, 1)));  

            doWork();//工作了  

            System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date(2017, 11, 14)));  

            latch.countDown();//工人完成工作,计数器减一  

  

        }  

        private void doWork(){  

            try {  

                Thread.sleep(workTime);  

            } catch (InterruptedExceptione) {  

                e.printStackTrace();  

            }  

        }  

    }        

}  

 

输出结果:

 

 

实现原理:

根据count=2,计数达到零后结束线程,输出工人的工作时间。

 

示例2:

模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。

package lichun;

 

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class CountDownLatchTest {

    public static void main(String[]args)throws InterruptedException {

        // 开始的倒数锁

        final CountDownLatchbegin =new CountDownLatch(1);  

        // 结束的倒数锁

        final CountDownLatchend =new CountDownLatch(10);  

        // 十名选手

        final ExecutorServiceexec = Executors.newFixedThreadPool(10);  

 

        for (int index = 0;index < 10; index++) {

            final int NO =index + 1;  

            Runnable run = new Runnable() {

                public void run() {  

                    try {  

                        // 如果当前计数为零,则此方法立即返回。

                        // 等待

                        begin.await();  

                        Thread.sleep((long) (Math.random() * 10000));  

                        System.out.println("No." +NO +" arrived");  

                    } catch (InterruptedExceptione) {  

                    } finally {  

                        // 每个选手到达终点时,end就减一

                        end.countDown();

                    }  

                }  

            };  

            exec.submit(run);

        }  

        System.out.println("Game Start");  

        // begin减一,开始游戏

        begin.countDown();  

        // 等待end变为0,即所有选手到达终点

        end.await();  

        System.out.println("Game Over");  

        exec.shutdown();  

    }

}

 

输出结果:

 

 

实现原理:

开始的倒数锁设为1,当它计数到0时开始输出结果;结束的倒数锁设为10,即等待end计数到0时,输出的十名选手的到达情况也随之结束。

 

3)总结

   通过这次对CountDownLatch概念和应用的探索,我比较理解了这个类,它虽然被定义为辅助类型,但是仍然是一个十分实用的计数计时类。它的原理如图所示:

课外作业之CountDownLatch应用详解

那它在什么时候使用呢:

 

1. 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。

2. 开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。

3. 死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。

 

这些收获都来自于在网上查找博客,百度和阅读JAVA_API_CN手册,从而我知道了专业知识不仅仅来源于课堂,课堂之外还有更多丰富的资源等着我们去发现去探索,大学中更重要的是自学,去挖掘课堂外的知识,不再是和高中一样,做一只嗷嗷待哺的窝中小鸟。

 

4)参考文献

JAVA_API_CN手册

http://blog.****.net/shihuacai/article/details/8856370

http://www.iteye.com/topic/1002652

http://www.importnew.com/15731.html