Java并发编程一---闭锁

250 阅读5分钟

1、什么是闭锁?

  闭锁(latch)是一种Synchronizer(Synchronizer:是一个对象,它根据本身的状态调节线程的控制流。常见类型的Synchronizer包括信号量、关卡和闭锁)。
  闭锁可以延迟线程的进度直到线程线程到达终止状态。一个闭锁工作起来就像是一道大门:直到闭锁达到终点状态之前,门一直是关闭的,没有线程能够通过,在终点状态到来的时候,所有线程都可以通过。


2、应用场景

  闭锁可以用来确保特定活动直到其他的活动都完成后才开始发生,比如:
  • 确保一个计算不会执行,直到它所需要的资源被初始化
  • 确保一个服务不会开始,直到它依赖的其他服务都已经开始
  • 等待,直到活动的所有部分都为继续处理做好充分准备
  • 死锁检测,可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁

3、闭锁的实现

  CountDownLatch是一个同步辅助类,存在于java.util.concurrent包下,灵活的实现了闭锁,它允许一个或多个线程等待一个事件集的发生。
  CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数的值就会减1。当计数器值到达0时,它所表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

4、CountDownLatch原理

  CountDownLatch构造函数:
CountDownLatch(
int
count);


  构造器中计数值(count)就是闭锁需要等待的线程数量,这个值只能被设置一次。
  CountDownLatch类的方法:
  • void await():使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
  • boolean await(long timeout, TimeUnit unit):使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
  • void countDown():递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
  • long getCount():返回当前计数。
  • String toString():返回标识此锁存器及其状态的字符串。
  与CountDownLatch第一次交互是主线程等待其它的线程,主线程必须在启动其它线程后立即调用await方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。
  其他的N个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务,这种机制就是通过countDown()方法来完成的。每调用一次这个方法,在构造函数中初始化的count值就减1,所以当N个线程都调用了这个方法count的值等于0,然后主线程就能通过await方法,恢复自己的任务。

5、CountDownLatch使用案例

  下面介绍API中推荐的一种典型的用法。
  首先给出两个类,其中一组worker线程使用了两个倒计数锁存器。
  第一个类是一个启动信号,在driver为继续执行worker做好准备之前,它会阻止所有的worker继续执行。
  第二个类是一个完成信号,它允许driver在完成所有的worker之前一直等待。
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class Driver
{ // ...
private final static int N=10;
public static void main(String[] args) throws InterruptedException
{
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
}
private static void doSomethingElse()
{
//doSomethingElse;
}
}
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Worker implements Runnable[/align]{
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal)
{
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run()
{
try
{
startSignal.await();
doWork();
doneSignal.countDown();
}
catch (InterruptedException ex)
{
} // return;
}
void doWork()
{
// ...
}
}
  基于上面的模型,下面我实现了自己的应用场景:
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class CountDownLatchDemo
{
final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException
{
CountDownLatch startLatch = new CountDownLatch(1);//开始工作
CountDownLatch endLatch = new CountDownLatch(2);// 两个工人的协作
Worker worker1 = new Worker("zhang san", 5000,startLatch, endLatch);
Worker worker2 = new Worker("li si", 8000, startLatch,endLatch);
worker1.start();//
worker2.start();//
startLatch.countDown(); //工作开始
endLatch.await();// 等待所有工人完成工作
System.out.println("all work done at " + sdf.format(new Date()));
}
static class Worker extends Thread
{
String workerName;
int workTime;
CountDownLatch startLatch;
CountDownLatch endLatch;
public Worker(String workerName, int workTime, CountDownLatch startLatch,CountDownLatch endLatch)
{
this.workerName = workerName;
this.workTime = workTime;
this.startLatch=startLatch;
this.endLatch = endLatch;
}
public void run()
{
try
{
startLatch.await(); //等待开始工作时间
System.out.println("Worker " + workerName + " do work begin at "+ sdf.format(new Date()));
Thread.sleep(workTime);
}
catch (InterruptedException e)
{
// TODO 自动生成的 catch 块
e.printStackTrace();
}
finally
{
System.out.println("Worker " + workerName + " do work complete at " + sdf.format(new Date()));
endLatch.countDown();// 工人完成工作,计数器减一
}
}
}
}

  执行的结果为:
  



更多技术资讯可关注:gzitcast