闭锁
由于设定了state为一个整数大于0的值,全部进入同步队列;
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
countdownDown
public void countDown() {
sync.releaseShared(1);
}
直接将state置为0;
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;
}
}
会唤醒第一个节点,而共享模式下会依次唤醒后续节点
Semaphore
读锁
和上相比,state也是一样的,
不过请求的时候,如果请求到了直接就通过了,cas成功就直接通过,知道state小于0,挂起线程;
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//小于0了直接进入同步队列,除了头节点全部挂起,直到信号量又有了,
private void doAcquireSharedInterruptibly(int arg)
释放的时候又会加state,
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
CyclicBarrier
重入锁+等待队列
CyclicBarrier barrier = new CyclicBarrier(令牌数量);
barrier.await();//屏障点,消耗一个令牌,当令牌消耗到任务数量的时候,栅栏就会全部放开
barrier.reset();//重置,可以复用栅栏,不用重新创建栅栏对象
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
阑珊的应用
CyclicBarrier 的应用场景:
- CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的场景。例如:用一个 Excel
- 保存了用户所有银行流水,每个 Sheet 保存一个账户近一年的每笔银行流水,现在需要统计
- 用户的日均银行流水,先用多线程处理每个 Sheet 里面的银行流水,都执行完之后,得到每个
- Sheet 的日均银行流水,最后,再用 barrierAction 用这些线程的计算结果,计算出整个
- Excel 的日均银行流水