在多线程程序中,很多时候需要让多个线程相互合作完成一个任务,这要求线程间能够进行协调.
例如:任务A和B是完成一项工作的两个划分,只有A任务计算出结果后,任务B才能开始计算.我们再将任务A划分为4个子任务,交给4个线程并行执行,由于子任务有大小区分,处理小任务的线程有可能很快就执行完毕了,因此该任务需要等待其他线程执行完成后,才能继续向下执行任务B;这时候我们需要用到线程障栅.
障栅
CyclicBarrier 类是一个同步辅助类,实现了一个称为障栅的集合点,在不是所有线程都到达集合点前,线程之前可以相互等待.Cyclic 的含义是“循环的,周期的”,代表该障栅在所有等待的线程到达集合点并被释放后可以循环使用.
CyclicBarrier 类比较适合于线程数量固定的情况.
CyclicBarrier 类的构造方法:
1、CyclicBarrier(int parties) //创建障栅对象;parties为需要等待的线程个数.2、CyclicBarrier(int parties,Runnable barrierAction) // barrierAction 定义最后一个进入障栅的线程要执行的动作.
CyclicBarrier 类的常用方法:
- int await() 在此障栅上的线程调用该方法后将等待
- int await(long timeout, TimeUnit unit)如果在指定时间内达到parties的数量则继续运行,否则抛出超时异常.
- int getParties() 障栅对象的parties个数.
- int getNumberWaiting() 在障栅处等待的线程个数.
- void reset() 重置障栅到初始状态.
在障栅的对象上可以调用 await() 方法,该方法需要放置到 try…catch…语句块中,并捕获 InterruptedException 和 BrokenBarrierException 异常.线程在完成自己的工作后调用 await() 方法等待.当最后一个线程调用 await() 方法后,将唤醒所有等待线程,并继续该障栅后的工作.
倒计时门闩
倒计时门闩就像一个带有计数器开关的门,只有在门前等待的线程到达一定数量时,门闩才会打开,线程才可以继续执行.
倒计时门闩由 CountDownLatch 类实现,该类从 Object 继承而来,可以通过一个给定的值进行初始化,通常在同步状态中保存的是当前的计数值,线程调用 await() 方法等待;方法 countDown() 会导致计数值递减,当计数值为零时,所有的倒计时门闩范围内的等待线程的阻塞状态将解除.
CountDownLatch 类的构造方法:
CountDownLatch(int count)
其中,count 为初始计数,必须为正数,否则将抛出 IllegalArgumentException 异常.
CountDownLatch 类常用的方法:
- public void await() //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
- public boolean await(long timeout, TimeUnit unit) //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
- public void countDown() { };//将count值减1
信号量
信号量机制通常用于限制对于某个资源同时访问的线程数量.
信号量机制是一种典型的同步机制,可以用于解决常用的线程同步问题.
在 Java 并发库中,类 Semaphore 可以实现信号量机制,其定义如下:
public class Semaphore extends Object implements Serializable
信号量管理了一个许可集合,可以通过方法 acquire() 获取一个许可,如果没有许可则可以等待.通过方法 release() 可以释放一个许可.
它的构造方法如下:
Semaphore(int permits) // 用给定的许可数创建一个 Semaphore 对象.
Semaphore(int permits,boolean fair) // 设置许可分配策略:公平分配/非公平分配
Semaphore 类常用的方法
- acquire() 获取一个许可,如果没有许可可以获取,则阻塞.
- void acquire(int permits) 获取permits个许可,如果没有许可可以获取,则阻塞.
- void acquireUninterruptibly() 从此信号量中获取许可,在有可用的许可前将其阻塞.
- int availablePermits() 获得可用的许可数
- int drainPermits() 获取并返回立即可用的许可数
- int getQueueLength() 返回等待获取许可的线程队列长度
- boolean hasQueuedThreads() 线程等待队列中是否有线程等待获取许可
- void release() 释放一个许可
- void release(int permits) 释放指定数量的许可
使用场景:
银行设置了4个窗口可以同时办理业务,现在有20个顾客需要办理业务,使用信号量机制可以实现这种场景.