190306-CyclicBarrier和CountDownLatch区别

CyclicBarrier和CountDownLatch的区别

CyclicBarrier和CountDownLatch对比

CountDownLatch CyclicBarrier
减数计数方式 加计数方式
计算为0时释放所有等待的线程 计数到达指定值时释放所有等待线程
计数为0时,无法重置 技术叨叨指定值时,计数置为0重新开始
调用countdown()方法计数减一,调用await()方法只进程阻塞,对计数没有任何影响 调用await()方法计数+1,若加1后的值不等于构造方法的值,则线程阻塞
不可重复利用 可重复利用

CountDownLatch

Countdownlatch是一个同步工具类;用来协调多个线程之间的同步;

这个工具通常用来控制线程等待;它可以让某一个线程等待知道倒计时结束,在开始执行;

  1. 某一线程在开始运行前等待n个线程执行完毕;将CountDownLatch的计数器初始化为n:new CountDownLatch(n);每当一个任务线程执行完毕,就将计数器减1,CountDownLatch.Countdown;当计数器的值变为0时;在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕;

  2. 实现多个线程开始执行任务的最大并行性;注意是并行性;而不是并发性;强调的是多个线程在某一时刻同时执行,类似于赛跑;将多个线程放到起点;等待发令枪响;然后同时开跑;做法是初始化一个共享的CountDownLatch对象;将其计数器初始化为1:new CountdownLatch(1);多个线程在开始执行任务前首先CountDownLatch.await(),当主线程调用countdownl时,计数器变为0;多个线程同时被唤醒;

CountDownLatch实例

有五个人,一个裁判。这五个人同时跑,裁判开始计时,五个人都到终点了,裁判喊停,然后统计这五个人从开始跑到最后一个撞线用了多长时间。

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
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
54
55
56
57
58
59
60
61
62
63
64
65
66
import java.util.concurrent.CountDownLatch;

public class Race {

public static void main(String[] args) {
final int num = 5;
final CountDownLatch begin = new CountDownLatch(1);
final CountDownLatch end = new CountDownLatch(num);

for (int i = 0; i < num; i++) {
new Thread(new AWorker(i, begin, end)).start();
}

// judge prepare...
try {
Thread.sleep((long) (Math.random() * 5000));
} catch (InterruptedException e1) {
e1.printStackTrace();
}

System.out.println("judge say : run !");
begin.countDown();
long startTime = System.currentTimeMillis();

try {
end.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
long endTime = System.currentTimeMillis();
System.out.println("judge say : all arrived !");
System.out.println("spend time: " + (endTime - startTime));
}

}

}

class AWorker implements Runnable {
final CountDownLatch begin;
final CountDownLatch end;
final int id;

public AWorker(final int id, final CountDownLatch begin,
final CountDownLatch end) {
this.id = id;
this.begin = begin;
this.end = end;
}

@Override
public void run() {
try {
System.out.println(this.id + " ready !");
begin.await();
// run...
Thread.sleep((long) (Math.random() * 10000));
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(this.id + " arrived !");
end.countDown();
}
}

}

CountDownLatch强调的是一个线程(或多个)需要等待另外的n个线程干完某件事情之后才能继续执行。 上述例子,main线程是裁判,5个AWorker是跑步的。运动员先准备,裁判喊跑,运动员才开始跑(这是第一次同步,对应begin)。5个人谁跑到终点了,countdown一下,直到5个人全部到达,裁判喊停(这是第二次同步,对应end),然后算时间。

Countdownlatch的不足

Countdownlatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,他不能再次被使用

CyclicBarrier

CyclicBarrier和CountDownLatch非常类似,他也可以实现线程间的技术等待,但是它的功能比CountDownLatch更加强大和复杂。主要应用场景和CountDownLatch类似

CyclicBarrier的字面意思是可循环使用的屏障,它要做的事情是,让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法时CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障。然后当前线程被阻塞;

CyclicBarrier的应用场景

CyclicBarrier可以用于多线程计算数据,最后合并结果的应用场景。比如我们用一个Excel保存了用户所有银行流水,每个Sheet保存每一个账户近一年的每一笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个sheet里的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,使用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水;

CyclicBarrier实例

继续,还是这五个人(这五个人真无聊..),这次没裁判。规定五个人只要都跑到终点了,大家可以喝啤酒。但是,只要有一个人没到终点,就不能喝。 这里也没有要求大家要同时起跑(当然也可以,加latch)

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Beer {
public static void main(String[] args) {
final int count = 5;
final CyclicBarrier barrier = new CyclicBarrier(count, new Runnable() {
@Override
public void run() {
System.out.println("drink beer!");
}
});
// they do not have to start at the same time...
for (int i = 0; i < count; i++) {
new Thread(new Worker(i, barrier)).start();
}
}

}

class Worker implements Runnable {
final int id;
final CyclicBarrier barrier;
public Worker(final int id, final CyclicBarrier barrier) {
this.id = id;
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println(this.id + "starts to run !");
Thread.sleep((long) (Math.random() * 10000));
System.out.println(this.id + "arrived !");
this.barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}

CyclicBarrier强调的是n个线程,大家相互等待,只要有一个没完成,所有人都得等着。正如上例,只有5个人全部跑到终点,大家才能开喝,否则只能全等着。

CountDownLatch是计数器,只能使用一次,而CyclicBarrier的计数器提供reset功能,可以多次使用

JavaDoc中的定义

  1. CountDownLatch:一个或者多个线程,等待其他线程完成某件事情之后,等待其他多个线程完成某件事情之后才能执行;

  2. CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行;
    对于CountDownLatch来说,重点是一个线程(多个线程)等待”,而其他的N个线程在完成”某件事情”之后,可以终止,也可以等待,而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待;

  3. CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都要到达,阀门才能打开,然后继续执行;

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×