字面意思回环栅栏(循环屏障),通过它可以实现让一组线程等待至某个状态(屏障点)之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。
CyclicBarrier的使用
构造方法
1
// parties
表示屏障拦截的线程数量,每个线程调用
await
方法告诉
CyclicBarrier
我已经
到达了屏障,然后当前线程被阻塞。
2
public
CyclicBarrier
(
int parties
)
3
//
用于在线程到达屏障时,优先执行
barrierAction
,方便处理更复杂的业务场景
(
该线程的
执行时机是在到达屏障之后再执行
)
4
public
CyclicBarrier
(
int parties
,
Runnable barrierAction
)
重要方法
1
//
屏障 指定数量的线程全部调用
await()
方法时,这些线程不再阻塞
2
// BrokenBarrierException
表示栅栏已经被破坏,破坏的原因可能是其中一个线程
await()
时被中断或者超时
3
public
int
await
()
throws InterruptedException
,
BrokenBarrierException
4
public
int
await
(
long timeout
,
TimeUnit unit
)
throws InterruptedException
,
Bro
kenBarrierException
,
TimeoutException
5
6
//
循环 通过
reset()
方法可以进行重置
7
public void
reset
()
CyclicBarrier应用场景
CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的场景。
1
public class
CyclicBarrierTest2
{
2
3
//
保存每个学生的平均成绩
4
private
ConcurrentHashMap
<
String
,
Integer
>
map
=
new
ConcurrentHashMap
<
String
,
I
nteger
>
();
5
6
private
ExecutorService threadPool
=
Executors
.
newFixedThreadPool
(
3
);
7
8
private
CyclicBarrier cb
=
new
CyclicBarrier
(
3
,()
‐>
{
9
int result
=
0
;
10
Set
<
String
>
set
=
map
.
keySet
();
11
for
(
String s
:
set
){
12
result
+=
map
.
get
(
s
);
13
}
14
System
.
out
.
println
(
"
三人平均成绩为
:"
+
(
result
/
3
)
+
"
分
"
);
15
});
16
17
18
public void
count
(){
19
for
(
int i
=
0
;
i
<
3
;
i
++
){
20
threadPool
.
execute
(
new
Runnable
(){
21
22
@Override
23
public void
run
() {
24
//
获取学生平均成绩
25
int score
=
(
int
)(
Math
.
random
()
*
40
+
60
);
26
map
.
put
(
Thread
.
currentThread
().
getName
(),
score
);
27
System
.
out
.
println
(
Thread
.
currentThread
().
getName
()
28
+
"
同学的平均成绩为:
"
+
score
);
29
try
{
30
//
执行完运行
await(),
等待所有学生平均成绩都计算完毕
31
cb
.
await
();
32
}
catch
(
InterruptedException
|
BrokenBarrierException e
) {
33
e
.
printStackTrace
();
34
}
35
}
36
37
});
38
}
39
}
40
41
public static void
main
(
String
[]
args
) {
42
CyclicBarrierTest2 cb
=
new
CyclicBarrierTest2
();
43
cb
.
count
();
44
}
45
}
利用CyclicBarrier的计数器能够重置,屏障可以重复使用的特性,可以支持类似“人满发车”的
场景
1
public class
CyclicBarrierTest3
{
2
3
public static void
main
(
String
[]
args
) {
4
5
AtomicInteger counter
=
new
AtomicInteger
();
6
ThreadPoolExecutor threadPoolExecutor
=
new
ThreadPoolExecutor
(
7
5
,
5
,
1000
,
TimeUnit
.
SECONDS
,
8
new
ArrayBlockingQueue
<>
(
100
),
9
(
r
)
‐>
new
Thread
(
r
,
counter
.
addAndGet
(
1
)
+
"
号
"
),
10
new
ThreadPoolExecutor
.
AbortPolicy
());
11
12
CyclicBarrier cyclicBarrier
=
new
CyclicBarrier
(
5
,
13
()
‐>
System
.
out
.
println
(
"
裁判:比赛开始
~~"
));
14
15
for
(
int i
=
0
;
i
<
10
;
i
++
) {
16
threadPoolExecutor
.
submit
(
new
Runner
(
cyclicBarrier
));
17
}
18
19
}
20
static class
Runner
extends
Thread
{
21
private
CyclicBarrier cyclicBarrier
;
22
public
Runner
(
CyclicBarrier cyclicBarrier
) {
23
this
.
cyclicBarrier
=
cyclicBarrier
;
24
}
25
26
@Override
27
public void
run
() {
28
try
{
29
int sleepMills
=
ThreadLocalRandom
.
current
().
nextInt
(
1000
);
30
Thread
.
sleep
(
sleepMills
);
31
System
.
out
.
println
(
Thread
.
currentThread
().
getName
()
+
"
选手已就位
,
准备共用
时:
"
+
sleepMills
+
"ms"
+
cyclicBarrier
.
getNumberWaiting
());
32
cyclicBarrier
.
await
();
33
34
}
catch
(
InterruptedException e
) {
35
e
.
printStackTrace
();
36
}
catch
(
BrokenBarrierException e
){
37
e
.
printStackTrace
();
38
}
39
}
40
}
41
42
}
CyclicBarrier与CountDownLatch的区别
7. CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset() 方
法重置。
所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重
置计数器,并让线程们重新执行一次
8. CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、
isBroken(用来知道阻塞的线程是否被中断)等方法。
9. CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
10. CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不
同。CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后,再执行。
CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
11. CyclicBarrier 还可以提供一个 barrierAction,合并多线程计算结果。
12. CyclicBarrier是通过ReentrantLock的"独占锁"和Conditon来实现一组线程的阻塞唤
醒的,而CountDownLatch则是通过AQS的“共享锁”实现