一、 Semaphore

字面意思为 信号量。Semaphore 用来控制某段代码块的并发数。 Semaphore 管理着一组虚拟的许可(permit),permit 的初始数量可通过构造方法来指定。每次执行 acquire 方法可以获取一个 permit,如果没有就等待;而 release 方法可以释放一个 permit。

Semaphore 应用场景:

  • Semaphore 可以用于实现资源池,如数据库连接池。
  • Semaphore 可以用于将任何一种容器变成有界阻塞容器。

Semaphore 提供了 2 个构造方法:

1
2
3
4
// 参数 permits 表示许可数目,即同时可以允许多少线程进行访问
public Semaphore(int permits) {}
// 参数 fair 表示是否是公平的,即等待时间越久的越先获取许可
public Semaphore(int permits, boolean fair) {}

说明:
permits - 初始化固定数量的 permit,并且默认为非公平模式。
fair - 设置是否为公平模式。所谓公平,是指等待久的优先获取 permit。

Semaphore 的重要方法:

1
2
3
4
5
6
7
8
// 获取 1 个许可
public void acquire() throws InterruptedException {}
//获取 permits 个许可
public void acquire(int permits) throws InterruptedException {}
// 释放 1 个许可
public void release() {}
//释放 permits 个许可
public void release(int permits) {}

说明:

  • acquire() - 获取 1 个 permit。在提供一个许可前一直将线程阻塞,或者线程被中断,
  • acquire(int permits) - 获取 permits 数量的 permit。
  • release() - 释放 1 个 permit。释放一个(多个)许可,将其返回给信号量
  • release(int permits) - 释放 permits 数量的 permit。

二、CountDownLatch

字面意思为 递减计数锁。用于控制一个线程等待多个线程。
CountDownLatch 维护一个计数器 count,表示需要等待的事件数量。countDown 方法递减计数器,表示有一个事件已经发生。调用 await 方法的线程会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时。

CountDownLatch 是基于 AQS(AbstractQueuedSynchronizer) 实现的。

CountDownLatch 唯一的构造方法:

1
2
// 初始化计数器
public CountDownLatch(int count) {};

说明:
count 为统计值。

CountDownLatch 的重要方法:

1
2
3
public void await() throws InterruptedException { };
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
public void countDown() { };

说明:
await() - 调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行。 await(long timeout, TimeUnit unit) - 和 await() 类似,只不过等待一定的时间后 count 值还没变为 0 的话就会继续执行; countDown() - 将统计值 count 减 1

三、CyclicBarrier

字面意思是循环栅栏CyclicBarrier 可以让一组线程等待至某个状态(遵循字面意思,不妨称这个状态为栅栏)之后再全部同时执行。之所以叫循环栅栏是因为:当所有等待线程都被释放以后,CyclicBarrier 可以被重用。
CyclicBarrier 维护一个计数器 count。每次执行 await 方法之后,count 加 1,直到计数器的值和设置的值相等,等待的所有线程才会继续执行。

CyclicBarrier 是基于 ReentrantLock 和 Condition 实现的。

CyclicBarrier 应用场景:CyclicBarrier 在并行迭代算法中非常有用。

CyclicBarrier 提供了 2 个构造方法

1
2
public CyclicBarrier(int parties) {}
public CyclicBarrier(int parties, Runnable barrierAction) {}

说明:

  • parties - parties 数相当于一个阈值,当有 parties 数量的线程在等待时, CyclicBarrier 处于栅栏状态。
  • barrierAction - 当 CyclicBarrier 处于栅栏状态时执行的动作。

CyclicBarrier 的重要方法:

1
2
3
4
5
6
7
public int await() throws InterruptedException, BrokenBarrierException {}
public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {}
// 将屏障重置为初始状态
public void reset() {}

说明:

  • await() - 等待调用 await() 的线程数达到屏障数。如果当前线程是最后一个到达的线程,并且在构造函数中提供了非空屏障操作,则当前线程在允许其他线程继续之前运行该操作。如果在屏障动作期间发生异常,那么该异常将在当前线程中传播并且屏障被置于断开状态。
  • await(long timeout, TimeUnit unit) - 相比于 await() 方法,这个方法让这些线程等待至一定的时间,如果还有线程没有到达栅栏状态就直接让到达栅栏状态的线程执行后续任务。 reset() - 将屏障重置为初始状态。

四、CyclicBarrier 与 CountDownLatch 区别

  1. CountDownLatch允许一个或者多个线程一直等待,直到这些线程完成他们的操作;而CyclicBarrier是当线程达到某种状态后,暂停下来等待其他线程,等到其他线程都到达后才继续执行。
  2. 等待主体不一样。CountDownLatch等待主体是调用 await() 的主线程或者调用线程;而 CyclicBarrier调用await 是在任务线程调用的。所以CyclicBarrier 中阻塞的是任务线程,主线程不受影响。
  3. 都是基于 AQS 实现的。但是 CyclicBarrier是直接借用 ReentrantLock加上 Condition 等待唤醒功能进而实现的。
  4. CyclicBarrier是可重用的,后者则不能。