java.util.concurrent
包包含了几个能帮助人们管理相互合作的线程集的类。这些机制具有为线程之间的共用集结点模式提供的预置功能。如果有一个相互合作的线程集满足这些行为模式之一,那么应该直接重用合适的库类而不要试图提供手工锁与条件结合。
一.信号量
- 概念上讲,一个信号量管理许多的许可证明。为了通过信号量,线程通过调用
acquire
请求许可。其实没有实际的许可对象,信号量仅维护一个计数。许可的数目是固定的,由此限制了通过的线程数量。其它的线程可以通过调用release释放许可。而且,许可不是必须由获取它的线程释放。事实上,任何线程都可以释放任意数目的许可,这可能会增加许可数目以至于超出初始数目。
二.倒计时门栓
- 一个倒计时门栓让一个线程集等待直到计时变为0.倒计时门栓是一次性的。一旦计数为0,就不能再重用了。
- 一个有用的特例是计数值为1的门栓。实现一个只能通过一次的门。线程在门外等候直到另一个线程将计数器值置为0。
- 举例来讲,假定一个线程集需要一些初始的数据来完成工作。工作器线程被启动并在门外等候。另一个线程准备数据。当数据准备好的时候,调用
countDown
,所有工作器线程就可以继续运行了。 - 然后,可以使用第二个门栓检查什么时候所有工作器线程完成工作。用线程数初始化门栓。每个工作器线程在结束前将门栓计数减一,另一个获取工作结果的线程在门外等待,一旦所有工作器线程终止该线程继续运行。
三.障栅
CyclicBarrier
类实现了一个集结点称为障栅。考虑大量线程运行在一次计算不同部分的情形。当所有部分都准备好时,需要把结果组合在一起。当一个线程完成了它的那部分任务后,我们让它运行到障栅处。一旦所有的线程都到达了这个障栅,障栅就撤销,线程就可以继续运行。
public class CyclicBarrierTest implements Runnable{
//构造一个障栅,并给出参与的线程数
private CyclicBarrier barrier = new CyclicBarrier(10);
@Override
public void run() {
//doWork();
try {
//每个线程做一些工作,完成后在障栅上调用await
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
//
}
}
- 如果任何一个在障栅上等待的线程离开了障栅,那么障栅就被破坏了(线程可能离开是因为它调用了await时设置了超时,或者因为它被中断了)。在这种情况下,所有其它线程的await方法抛出
BrokenBarrierException
异常。那些已经在等待的线程立即终止await
调用。 - 可以提供一个可选的障栅动作,当所有线程到达障栅的时候就会执行这一动作。
Runnable barrierAction = ...;
CyclicBarrier barrier = new CyclicBarrier(10, barrierAction);
- 该动作可以收集那些单个线程的运行结果。障栅被称为是循环的,因为可以在所有等待线程被释放后被重用。在这一点上,有别于
CountDownLatch
,CountDownLatch
只能被使用一次。 Phaser
类增加了更大的灵活性,运行改变不同阶段中参与线程的个数。
四.交换器
- 当两个线程在同一个数据缓冲区的两个实例上工作的时候,就可以使用交换器。典型的情况是,一个线程向缓冲区填入数据,另一个线程消耗这些数据。当它们都完成后,相互交换缓冲区。
五.同步队列
- 同步队列是一种将生产者与消费者线程配对的机制。当一个线程调用
SynchronousQueue
的put
方法时,它会阻塞直到另一个线程调用take
方法为止,反之亦然。与Exchanger
的情况不同,数据仅仅沿着一个方向传递,从生产者到消费者。 - 即使
SynchronousQueue
类实现了BlockingQueue
接口,概念上讲,它依然不是一个队列。它没有包含任何元素,它的size
方法返回总是0。