多线程并发通信工具类
Semaphore
定义
限制线程的数量,往往用于资源有限的场景中,限制线程的数量。
// 默认情况是使用非公平
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
最主要的方法是 acquire
方法和 release
方法。acquire()
方法会申请一个 permit,而 release
方法会释放一个 permit。 当然,也可以申请或者释放多个 permits。当permits 为0时,再有其他线程来 acquire,那就要阻塞这个线程直到有其它线程 release permit 为止。
原理
public void acquire() throws InterruptedException {
// 根据公平或者非公平选择调用 acquireSharedInterruptibly,
// 在共享模式下获取,如果中断则会终止
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 尝试去获取资源,不进入等待队列,只使用 CAS
// >= 0代表正常获取返回还剩下的资源,< 0 表示获取失败
if (tryAcquireShared(arg) < 0)
// 获取失败,将当前线程放入 AQS 等待队列
doAcquireSharedInterruptibly(arg);
}
Exchanger
定义
用于两个线程交换数据,支持泛型。
原理
当一个线程调用 exchange 方法后,它是处于阻塞状态的,只有当另外一个线程也调用 exchange 方法,它才会继续向下执行。底层是使用Park/UnPark来实现等待状态的切换,但在执行 Park/Unpark 之前首先会进行CAS 检查,是为了提高性能。可重复使用 exchange 方法。
CountDownLatch
定义
线程等待直到计数器减为0时开始工作,线程在执行任务之前,需要等待其它线程完成一些前置任务,必须等待所有的前置任务都完成,才能开始本线程的任务。
原理
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// CAS 将 设置state = state - arg,如果为0 则
if (tryReleaseShared(arg)) {
// 释放锁,唤醒阻塞线程
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
CyclicBarrier
定义
允许一组线程全部等待彼此达到共同屏障点的同步辅助。循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。意思就是每个线程都得执行到等待点进行等待,直到所有线程都执行到等待点,才会继续往下执行。作用跟CountDownlatch类似,但是可以重复使用。
原理
底层使用ReentrantLock+Condition进行锁状态的维护
//同步操作锁
private final ReentrantLock lock = new ReentrantLock();
//线程拦截器
private final Condition trip = lock.newCondition();
//每次拦截的线程数
private final int parties;
//换代前执行的任务
private final Runnable barrierCommand;
//表示栅栏的当前代
private Generation generation = new Generation();
//计数器
private int count;
//静态内部类Generation
private static class Generation {
boolean broken = false;
}
CyclicBarrier(int parties) {
this(parties, null);
}
CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public int await() {
return dowait(false, 0L);
}
public int await(long timeout, TimeUnit unit){
return dowait(true, unit.toNanos(timeout));
}
private int dowait(boolean timed, long nanos){
final ReentrantLock lock = this.lock;
1、获取锁
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
2、如果线程中断,重置等待线程数量并且唤醒当前等待的线程
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
3、等待线程数减1
int index = --count;
4、当等待线程数为 时
if (index == 0) { // tripped
boolean ranAction = false;
try {
5、执行所有线程都到达等待点之后的Runnable
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
6、唤醒所有线程并生成下一代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
7、如果等待线程数不为0
for (;;) {
try {
8、根据传入的参数来决定是定时等待还是非定时等待
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
9、线程中断之后唤醒所有线程并进入下一代
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
10、如果线程因为打翻屏障操作而被唤醒则抛出异常
if (g.broken)
throw new BrokenBarrierException();
11、如果线程因为换代操作而被唤醒则返回计数器的值
if (g != generation)
return index;
12、如果线程因为时间到了而被唤醒则打翻栅栏并抛出异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
可以看到,是通过index字段控制线程等待的,当index不为0的时候,线程统一会进行阻塞,直到index为0的时候,才会唤醒所有线程,这时候所有线程才会继续往下执行。
重复使用
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
1、破坏当前的屏障点并唤醒所有线程
breakBarrier();
2、生成下一代
nextGeneration();
} finally {
lock.unlock();
}
}
private void breakBarrier() {
generation.broken = true;
将等待线程数量重置
count = parties;
唤醒所有线程
trip.signalAll();
}
private void nextGeneration() {
唤醒所有线程
trip.signalAll();
将等待线程数量重置
count = parties;
generation = new Generation();
}