1. 引言
之前对同步工具类闭锁CountDownLatch、信号量Semaphore、栅栏CycliBarrier有过了解,但是对其原理还不是清晰,在此从源码角度进行分析。
详细的实例代码地址:https://github.com/yq-debug/JavaExercise/tree/master/src/main/java/juc_sync
2. CountDownLatch
//允许一个线程或多个线程等待,直到其他线程的操作完成
public class CountDownLatch {
//Sync类继承自AQS类
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//初始化Sync类
Sync(int count) {
setState(count);//设置同步状态
}
//获取同步状态
int getCount() {
return getState();
}
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;
//构造一个含有count的锁存器的对象
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
//初始化sync对象,此时传入的是锁存器的数量
this.sync = new Sync(count);
}
//阻塞当前线程直到线程阻塞器的计数为0
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//阻塞当前线程直到线程阻塞器的计数为0
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//减少锁存器的数量
public void countDown() {
sync.releaseShared(1);
}
//返回当前锁存器的数量
public long getCount() {
return sync.getCount();
}
//返回此闭锁的字符串表示
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
2.1. 理解
-
CountDownLatch的作用是阻塞当前线程,直到其他线程操作完成之后,即闭锁中锁存器的数量为0时,再激活当前线程继续执行,与join()方法类似,都是将并行线程变为串行线程。
-
闭锁CountDownLatch在初始化的时候会传入一个参数,表示锁存器的数量,即代表要阻塞的线程的数量。
-
需要后执行的线程先进行阻塞,需要先行执行的线程在执行完之后将锁存器的数量减1,直到锁存器的数量为0或者时间超时触发阻塞线程执行。
此处使用的两个方法:countDown()锁存器的数量减1, await()阻塞当前线程直到锁存器的数量为0
-
使用场景:当线程的执行执行结果与执行顺序有关时(即会出现竞争冲突)则采用闭锁CountDownLatch使并发线程变为串行线程执行。
-
CountDownLatch的底层实现是AQS类
3. Semaphore
public class Semaphore implements java.io.Serializable {
private static final long serialVersionUID = -3222578661600680210L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {