最近一直整并发这块东西,顺便写点Java并发的例子,给大家做个分享,也强化下自己记忆,如果有什么错误或者不当的地方,欢迎大家斧正。
CyclicBarrier是一种多线程并发控制实用工具,和CountDownLatch非常类似,它也可以实现线程间的计数等待,但是它的功能比CountDownLatch更加复杂且强大。
CyclicBarrier的介绍
CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。线程进入屏障通过CyclicBarrier的await()方法。
CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction这个Runnable对象,方便处理更复杂的业务场景。
1
2
3
4
5
6
|
public CyclicBarrier( int parties) { this (parties, null ); } public int getParties() { return parties; } |
实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用CyclicBarrier的await方法时,将剩余拦截的线程数减1,然后判断剩余拦截数是否为0,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁,接着先从await方法返回,再从CyclicBarrier的await方法中返回。
CyclicBarrier主要用于一组线程之间的相互等待,而CountDownLatch一般用于一组线程等待另一组些线程。实际上可以通过CountDownLatch的countDown()和await()来实现CyclicBarrier的功能。即 CountDownLatch中的countDown()+await() = CyclicBarrier中的await()。注意:在一个线程中先调用countDown(),然后调用await()。
构造函数CyclicBarrier可以理解为循环栅栏,这个计数器可以反复使用。比如,假设我们将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,然后接着凑齐下一批10个线程,这就是它的内在含义。
LOL和王者荣耀的玩家很多,许多人应该都有打大龙的经历,话说前期大家打算一起去偷大龙,由于前期大家都比较弱,需要五个人都齐了才能打大龙,这样程序该如何实现呢?本人很菜,开始我的代码是这么写的(哈哈大家不要纠结我的时间):
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
|
public class KillDragon { /** * 模拟打野去打大龙 */ public static void dayePlayDragon(){ System.out.println( "打野在去打大龙的路上,需要10s" ); } /** * 模拟上单去打大龙 */ public static void shangdanPlayDragon(){ System.out.println( "上单在去打大龙的路上,需要10s" ); } /** * 模拟中单去打大龙 */ public static void zhongdanPlayDragon(){ System.out.println( "中单在去打大龙的路上,需要10s" ); } /** * 模拟ADC和辅助去打大龙 */ public static void adcAndFuzhuPlayDragon(){ System.out.println( "ADC和辅助在去打大龙的路上,需要10s" ); } /** * 模拟大家一起去打大龙 */ public static void killDragon() { System.out.println( "打大龙..." ); } public static void main(String[] args) { dayePlayDragon(); shangdanPlayDragon(); zhongdanPlayDragon(); adcAndFuzhuPlayDragon(); killDragon(); } |
结果如下:
1
2
3
4
5
|
打野在去打大龙的路上,需要10s 上单在去打大龙的路上,需要10s 中单在去打大龙的路上,需要10s ADC和辅助在去打大龙的路上,需要10s 打大龙... |
这完了,大家在路上的时间就花了40s了,显然是错误的。要是都这么干,对方把你塔都要偷光了。不行得改进下,怎么改呢,多线程并发执行,如是我改成了下面这样的,用volatile关键字。
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
|
private static volatile int i = 4 ; public static void main(String[] args) { new Thread( new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); while (i!= 0 ){ } while (i== 0 ) { killDragon(); i--; long t = System.currentTimeMillis() - start; System.out.println( "总共耗时:" +t+ "毫秒" ); } } }).start(); new Thread( new Runnable() { @Override public void run() { dayePlayDragon(); try { Thread.sleep( 10000 ); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread( new Runnable() { @Override public void run() { shangdanPlayDragon(); try { Thread.sleep( 10000 ); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread( new Runnable() { @Override public void run() { zhongdanPlayDragon(); try { Thread.sleep( 10000 ); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread( new Runnable() { @Override public void run() { adcAndFuzhuPlayDragon(); try { Thread.sleep( 10000 ); i--; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } |
结果如下:
1
2
3
4
5
6
|
打野在去打大龙的路上,需要10s 上单在去打大龙的路上,需要10s 中单在去打大龙的路上,需要10s ADC和辅助在去打大龙的路上,需要10s 打大龙... 总共耗时: 10005 毫秒 |
结果似乎还不错,但是处理起来实在是有点麻烦,需要 while (i!=0)一直在那循环着。这时候学到了用 CyclicBarrier来处理,代码如下:
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
67
|
public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier( 5 ); new Thread( new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } killDragon(); long t = System.currentTimeMillis() - start; System.out.println( "总共耗时:" +t+ "毫秒" ); } }).start(); new Thread( new Runnable() { @Override public void run() { dayePlayDragon(); try { Thread.sleep( 10000 ); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread( new Runnable() { @Override public void run() { shangdanPlayDragon(); try { Thread.sleep( 10000 ); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread( new Runnable() { @Override public void run() { zhongdanPlayDragon(); try { Thread.sleep( 10000 ); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread( new Runnable() { @Override public void run() { adcAndFuzhuPlayDragon(); try { Thread.sleep( 10000 ); barrier.await(); } catch (Exception e) { e.printStackTrace(); } } }).start(); } |
大家都没到达之前都等待,结果如下:
1
2
3
4
5
6
|
打野在去打大龙的路上,需要10s 上单在去打大龙的路上,需要10s 中单在去打大龙的路上,需要10s ADC和辅助在去打大龙的路上,需要10s 打大龙... 总共耗时: 10002 毫秒 |
CyclicBarrier相当于线程的计数器:
CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。
CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。
CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
当然这样使用CyclicBarrier和使用CountDownLatch是没什么区别的,正如前文所说的CyclicBarrier的功能更加的复杂且强大。给大家看一个《实战Java高并发程序设计》一书上的一个例子。
比如:司令下达命令,要求10个士兵去一起完成一项任务。这时,就会要求10个士兵先集合报道,接着,一起雄赳赳气昂昂地去执行任务。当10个士兵都执行完了任务,那么司机就可以对外宣称,任务完成。相比CountDownLatch,CyclicBarrier可以接受一个参数作为BarrierAction。所谓的BarrierAction就是当计数器一次计数完成后,系统会执行的动作。如下构造函数,其中,parties表示技术总数,也就是参与的线程总数。
public CyclicBarrier(int parties, Runnable barrierAction)
下面示例演示了上述任务场景
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
|
public class CyclicBarrierDemo { public static class Soldier implements Runnable { private String soldier; private final CyclicBarrier cyclicBarrier; public Soldier(CyclicBarrier cyclicBarrier, String soldier) { this .soldier = soldier; this .cyclicBarrier = cyclicBarrier; } @Override public void run() { try { cyclicBarrier.await(); doWork(); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } void doWork() { try { Thread.sleep(Math.abs( new Random().nextInt() % 10000 )); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(soldier + ":任务完成" ); } } public static class BarrierRun implements Runnable { boolean flag; int N; public BarrierRun( boolean flag, int N) { this .flag = flag; this .N = N; } @Override public void run() { if (flag) { System.out.println( "司令:[士兵" + N + "个,任务完成!" ); } else { System.out.println( "司令:[士兵" + N + "个,集合完毕!" ); flag = true ; } } } public static void main(String args[]) { final int N = 10 ; Thread[] allSoldier = new Thread[N]; boolean flag = false ; CyclicBarrier cyclicBarrier = new CyclicBarrier(N, new BarrierRun(flag, N)); System.out.println( "集合队伍!" ); for ( int i = 0 ; i < N; i++) { System.out.println( "士兵" + i + "报道!" ); allSoldier[i] = new Thread( new Soldier(cyclicBarrier, "士兵" + i)); allSoldier[i].start(); } } } |
执行结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
集合队伍! 士兵 0 报道! 士兵 1 报道! 士兵 2 报道! 士兵 3 报道! 士兵 4 报道! 士兵 5 报道! 士兵 6 报道! 士兵 7 报道! 士兵 8 报道! 士兵 9 报道! 司令:[士兵 10 个,集合完毕! 士兵 0 :任务完成 士兵 2 :任务完成 士兵 9 :任务完成 士兵 3 :任务完成 士兵 7 :任务完成 士兵 8 :任务完成 士兵 1 :任务完成 士兵 4 :任务完成 士兵 5 :任务完成 士兵 6 :任务完成 司令:[士兵 10 个,任务完成! |
总结
以上就是本文关于Java并发实例之CyclicBarrier的使用的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。期待您的宝贵意见!
原文链接:http://www.cnblogs.com/WangHaiMing/p/7830961.html