等待多线程完成的CountDownLatch(门闩)

CountDownLatch 可以让某一个线程直到倒计时结束,才开始执行.

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

CountDownLatch允许一个或多个线程等待其他线程完成操作。
假如有这样一个需求:我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多
线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完
成。在这个需求中,要实现主线程等待所有线程完成sheet的解析操作,最简单的做法是使用
join()方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class JoinCountDownlatch
{
public static void main(String[] args) throws InterruptedException
{
Thread parser1 = new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println("1 finish");
}
});
Thread parser2 =new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println("2 finish");
}
});
parser1.start();
parser2.start();
parser1.join();
parser2.join();
System.out.println("all finished!");_
}
}

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

并发包的CountDownLatch 也可以实现join的功能,并且比join的功能更多.

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

1
public CountDownLatch(int count);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class JoinCountDownlatch
{
static CountDownLatch c =new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException
{
new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println(1);
c.countDown();
System.out.println(2);
c.countDown();
}
}).start();
c.await(); //主线程执行这条语句,所以主线程阻塞掉.
System.out.println(3);
}
}

当调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch 的await方法 会阻塞当前线程,直到N变为0

countDown可以用在任何地方,所以这里的N个点,可以是N个线程,也可以是一个线程里的N个执行步骤,用在多个线程中时,只需把这个CountDownLatch 的引用传递到线程里即可.

CountDownLatch的await 函数也有带指定之间的形式避免无限等待.

与CountDownLatch的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用CountDownLatch.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。

在主线程的代码中执行await操作就是主线程进行等待,在工作线程上执行就是工作线程执行

其他N 个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown()方法来完成的;每调用一次这个方法,在构造函数中初始化的count值就减1。所以当N个线程都调 用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。

另一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CountDownLatchDemo implements Runnable {
//表示有10个线程完成任务,等待在CountDownLatch上的线程才能继续执行.
static final CountDownLatch end =new CountDownLatch(10);
static final CountDownLatchDemo demo =new CountDownLatchDemo();
public void run()
{
try {
Thread.sleep(new Random().nextInt(10)*1000);
System.out.println("check comlete");
end.countDown();//通知 CountDownLatch, 一个线程已经完成了任务.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++)
{
executorService.submit(demo);
}
//等待检查
end.await();//要求主线程等待所有10个检查任务全部完成后,主线程才能继续执行.
System.out.println("Fire!");
executorService.shutdown();
}
}