闭锁
闭锁(CountDownLatch)是一种组合事件同步工具类,可以延迟线程的进度直到闭锁到达终止状态.
闭锁的作用相当于一扇门,在闭锁到达结束状态之前,这扇门一直是关闭的,任何线程都不能通过这扇门,当闭锁到达结束状态时,这扇门会打开并允许所有线程通过
当闭锁到达结束状态后,将不会再改变状态,这扇门将永远保持打开状态.
- 或事件,事件A || 事件B 发生 则打开门。 爸爸或妈妈敲门,我就开门。把Latch值设为1,爸爸或妈妈回来-1.
- 且事件,事件A &&事件B 发生 则打开门。找到2只袜子,才能穿。把Latch值设为2,找到一只-1.
- 权重事件,(事件A &&事件B || 事件C) 发生 则打开门.晴天朋友叫我打球或者老板叫我加班,我出门。把Latch设为2,老板叫则-2,晴天-1,朋友叫打球-1;
//AQS state的值就是闭锁的值
//充分利用AQS的共享模式
//重写tryAcquireShared,当且仅当资源为0才开始唤醒
//因为节点全为共享型,则会全部唤醒
public class CountDownLatch {
//继承AQS
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//尝试共享锁,如果资源不为0,则获取锁失败
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
//尝试拿共享锁
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//共享资源-1
public void countDown() {
sync.releaseShared(1);
}
}
计数信号量
计数信号量(counting semaphore)用来控制同时访问某个特定资源的操作数量,或者同时执行某个制定操作的数量。计数信号量还可以实现某种资源池,或者对容器施加边界。
//主要实现也是利用AQS的共享模式
//许可permits就是state
//可上共平锁和非公平锁
public class Semaphore implements java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
//什么都不管,还有许可我就抢了先,不共平
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//看当前线程占用了多少许可,+回去
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;
}
}
}
//不公平模式
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
//发现有人排队了就去排队
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
//申请许可
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//释放许可
public void release() {
sync.releaseShared(1);
}
}
栅栏
栅栏(Barrier)主要能阻塞一组线程直到某个事件发生,它和闭锁的的区别是所有的线程都必须同时到达栅栏时,才能继续执行,而闭锁是用于等待事件(人等事),栅栏是用于等待其他线程(事等人)。栅栏可以留着下次使用。
//主要利用ReentrantLock和Condition
public class CyclicBarrier {
//栅栏状态,默认为不是坏的
private static class Generation {
boolean broken = false;
}
//锁
private final ReentrantLock lock = new ReentrantLock();
//人满事件
private final Condition trip = lock.newCondition();
//当栅栏搞错后,要做的事情
private final Runnable barrierCommand;
private Generation generation = new Generation();
//要等待多少人,才能搞坏这栅栏
private int count;
//搞坏一个栅栏,然后继续下一个
private void nextGeneration() {
//人员已到齐,冲
trip.signalAll();
//这些人过去了,再重新修个
count = parties;
generation = new Generation();
}
//搞坏一个栅栏
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
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();
}
}
for (;;) {
try {
if (!timed)
//人没满,等待人满
trip.await();
else if (nanos > 0L)
//不等了
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}
//等人齐
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
}
总结
AQS是个神奇的设计,可以利用它造更多的同步工具。在选用工具的时候要根据对应的场景选用。