多线程分配线程的实现方案:CountDownLatch类

需求:假如我们本地有4个文件需要解析,每个文件的内容为20万行。为了提高效率我们要创建4个线程进行处理。等4个线程处理完,要在文件日志表中记录处理状态。

一般的的解决方法是使用join,join用于让当前执行线程等待join线程执行结束。其实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远wait。直到join线程中止后,线程的this.notifyAll会被调用。

 

但是DK1.5之后的并发包中提供的CountDownLatch也可以实现join的这个功能,并且比join的功能更多。

 1 public class CountDownLatchTest {
 2 
 3     static CountDownLatch c = new CountDownLatch(4);
 4 /**线程记录数,当线程开始调用时主线程阻塞,每当一个线程结束时调用countDown()方法,线程记录数减一,当线程记录数为0时,主线程恢复调用。***/
 5 
 6     public static void main(String[] args) throws InterruptedException {
 7         new Thread(new Runnable() {
 8             @Override
 9             public void run() {
10                 System.out.println(1);
11                 c.countDown();//记录数减一,3
12                 System.out.println(2);
13                 c.countDown();//记录数减一,2
14                                 System.out.println(3);
15                 c.countDown();//记录数减一,1
16                 System.out.println(4);
17                 c.countDown();//记录数减一,0
18             }
19         }).start();
20 
21         c.await();
22         System.out.println("主线程调用");//开始调用日志记录
23     }
24 
25 }                    

CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待N个点完成,这里就传入N。

当我们调用一次CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await会阻塞当前线程,直到N变成零。由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多个线程时,你只需要把这个CountDownLatch的引用传递到线程里。

 

 1     /**
 2      * 单个文件处理入库
 3      * @author yanjp
 4      *
 5      */
 6     class Worker extends Thread{
 7         private DyFile dyFile;
 8         private CountDownLatch ct;
 9         private AnalyzeFileService analyzeFileService;
10         public Worker(AnalyzeFileService analyzeFileService ,DyFile dyFile,CountDownLatch ct){
11             this.dyFile=dyFile;
12             this.ct=ct;
13             this.analyzeFileService=analyzeFileService;
14         }
15         @Override
16         public void run() {
17             // TODO Auto-generated method stub
18             try {
19                 logger.info("begin入库处理文件:"+dyFile.getFileName());
20                 //合适的处理器,处理文件.
21                 analyzeFileService.analyze(dyFile);
22                 logger.info("end入库处理文件:"+dyFile.getFileName());
23             } catch (Exception e) {
24                 // TODO Auto-generated catch block
25                 logger.error("单个文件入库处理异常",e);
26             }finally{
27                 //完成一件任务
28                 ct.countDown();
29             }
30         }
31         
32     }

如果有某个解析sheet的线程处理的比较慢,我们不可能让主线程一直等待,所以我们可以使用另外一个带指定时间的await方法,await(long time, TimeUnit unit): 这个方法等待特定时间后,就会不再阻塞当前线程。join也有类似的方法。

注意:计数器必须大于等于0,只是等于0时候,计数器就是零,调用await方法时不会阻塞当前线程。CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部计数器的值。一个线程调用countDown方法 happen-before 另外一个线程调用await方法。

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

参考:

http://ifeve.com/talk-concurrency-countdownlatch/

 

posted @ 2016-03-30 14:13  我是你的大树  阅读(625)  评论(0编辑  收藏  举报