CountDownLatch, CyclicBarrier, Semaphore 和 Phaser

CountDownLatch

计数与阻塞是分离的(相对活),当计数器为0时释放阻塞线程,不可重置,不可复用。

参与计数的线程不用阻塞,需要阻塞的线程不用参与计数。

主要方法

await:阻塞当前线程(可设置超时时间)

countdown:计数减1

getCount:返回当前计数

示例

 1 public class CountDownLatchTest {
 2 
 3     static final CountDownLatch countDownLatch = new CountDownLatch(5);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 5; i++) {
 7             new Thread(()->{
 8                 try {
 9                     Thread.sleep(new Double(Math.random() * 5000).longValue());
10                     System.out.println(Thread.currentThread().getName() + "计数");
11                     countDownLatch.countDown(); // 计数减1
12                     System.out.println(Thread.currentThread().getName() + "继续执行");
13                 } catch (InterruptedException e) {
14                     e.printStackTrace();
15                 }
16             }).start();
17         }
18         try {
19             System.out.println("主线程进入阻塞");
20             countDownLatch.await(); // 阻塞,计数器为0时释放
21             System.out.println("主线程开始执行");
22         } catch (InterruptedException e) {
23             e.printStackTrace();
24         }
25     }
26 }

执行结果

 

Cyclic Barrier

计数与阻塞是不分离的(不够灵活),当计数器达到指定值时释放,可重置,可复用。

参与计数的一定是阻塞线程,需要阻塞的线程一定参与计数。

主要方法

重载的构造方法:CyClicBarrier(int parties, Runnable barrierAction)(指定屏障操作)

await:阻塞当前线程,并且计数加1(可设置超时时间)

getNumberWaiting:返回当前阻塞的线程数目(当前计数)

getParties:返回计数器指定的值

reset:将循环屏障重置为初始状态(计数归0)

示例

 1 public class CyclicBarrierTest {
 2 
 3     static final CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 5; i++) {
 7             new Thread(()->{
 8                 try {
 9                     Thread.sleep(new Double(Math.random() * 5000).longValue());
10                     System.out.println(Thread.currentThread().getName() + "阻塞并且计数");
11                     cyclicBarrier.await(); // 阻塞并且计数加1
12                     System.out.println(Thread.currentThread().getName() + "继续执行第一部分");
13                     Thread.sleep(new Double(Math.random() * 5000).longValue());
14                     System.out.println(Thread.currentThread().getName() + "阻塞并且计数");
15                     cyclicBarrier.await(); // 阻塞并且计数加1
16                     System.out.println(Thread.currentThread().getName() + "继续执行第二部分");
17                 } catch (InterruptedException e) {
18                     e.printStackTrace();
19                 } catch (BrokenBarrierException e) {
20                     e.printStackTrace();
21                 }
22             }).start();
23         }
24     }
25 }

执行结果

 

Semaphore

类似锁机制,但本质依然是线程的调度,可以控制同时使用该信号量的线程个数。

主要方法

重载的构造方法:Semaphore(int permits, boolean fair)(指定公平性)

acquire:从此信号量获取一个许可,获取到许可之前线程将被阻塞

release:释放当前许可

availablePermits:返回此信号量的可用许可数目

示例

 1 public class SemaphoreTest {
 2 
 3     static final Semaphore semaphore = new Semaphore(2);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 5; i++) {
 7             new Thread(()->{
 8                 try {
 9                     semaphore.acquire(); // 获取一个许可
10                     System.out.println(Thread.currentThread().getName() + "开始执行");
11                     Thread.sleep(new Double(Math.random() * 5000).longValue());
12                     System.out.println(Thread.currentThread().getName() + "执行结束");
13                     semaphore.release(); // 释放当前许可
14                 } catch (InterruptedException e) {
15                     e.printStackTrace();
16                 }
17             }).start();
18         }
19     }
20 }

执行结果

 

Phaser

结合了CountDown和CyclicBarrier的特性,还具备分阶段的功能。

主要方法

arrive:抵达

awaitAdvance:阻塞等待某阶段结束

arriveAndAwaitAdvance:抵达并且阻塞等待

register:注册

arriveAndDeregister:抵达并且注销

getPhase:获得当前阶段数

示例

 1 public class PhaserTest {
 2 
 3     static final Phaser phaser = new Phaser(2);
 4 
 5     public static void main(String[] args) {
 6         for (int i = 0; i < 3; i++) {
 7             new Thread(()->{
 8                 try {
 9                     Thread.sleep(new Double(Math.random() * 5000).longValue());
10                     if (phaser.getPhase() == 0) {
11                         System.out.println(Thread.currentThread().getName() + "抵达第一阶段");
12                         phaser.arriveAndAwaitAdvance();
13                     }
14                     Thread.sleep(new Double(Math.random() * 5000).longValue());
15                     if (phaser.getPhase() <= 1) {
16                         System.out.println(Thread.currentThread().getName() + "抵达第二阶段");
17                         phaser.arriveAndAwaitAdvance();
18                     }
19                     Thread.sleep(new Double(Math.random() * 5000).longValue());
20                     if (phaser.getPhase() <= 2) {
21                         System.out.println(Thread.currentThread().getName() + "抵达第三阶段");
22                         phaser.arrive();
23                     }
24                 } catch (InterruptedException e) {
25                     e.printStackTrace();
26                 }
27             }).start();
28         }
29         phaser.awaitAdvance(0);
30         System.out.println("第一阶段结束");
31         phaser.register(); // 注册一个
32         phaser.awaitAdvance(1);
33         System.out.println("第二阶段结束");
34         phaser.arriveAndDeregister(); // 注销一个
35         phaser.awaitAdvance(2);
36         System.out.println("第三阶段结束");
37     }
38 }

执行结果

 

posted @ 2020-09-07 15:43  昆梧  阅读(305)  评论(0)    收藏  举报