CountDownLatch和CyclicBarrier源码解析

CountDownLatch和CyclicBarrier的主要区别:

1.CountDownLatch是1个线程等待另外多个线程,主线程要等待,其他线程可以继续做其他事情。

CyclicBarrier是多个线程互相等待,都不能做其他事情。

2.CountDownLatch不能重置,用一次就丢弃。

CyclicBarrier可以通过reset重置。


CyclicBarrier源码解析

CyclicBarrier是通过显示锁ReentrantLock来锁存对象


/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();


核心方法await()

private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;//主要用于判断barrier是否被破坏

if (g.broken)
throw new BrokenBarrierException();

if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}

int index = --count;//每有一个线程通过,index就会自减。
if (index == 0) { // tripper跳出等待。
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)//如果barrier构造的时候,附带一个Runnable,可以用于计时器
command.run();
ranAction = true;
nextGeneration();//其他所有线程的唤醒在这里。!
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}

//循环等待,直到跳出,中断,或者barrier被破坏。
for (;;) {
try {
if (!timed)
trip.await();//lock显示锁里的await方法。
else if (nanos > 0L)//如果设置了等待时间。
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// 一个弥补的解决方法,确保线程完全中断。
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();
}
}


barrier的步骤。n-1个线程进去await方法后,都在for(;;)等待。
直到最后一个线程进入await后,将其他所有线程唤醒。

CountDownLatch使用了AbstractQueuedSynchronizer作为辅助类,这是一个适用于所有使用int值作为state状态量的同步辅助类。

并且重写了
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}

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))//本地CAS方法,刷新state的值
return nextc == 0;
}
}

值得一提的是,如果不重写这2个方法,或抛出UnsupportedOperationException();因为在父类中如下定义
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}

跟abstract方法相比,这个非强制的,并且捕获的是一个RuntimeException

第一个核心方法countDown()主要是在上面那个方法中
if (compareAndSetState(c, nextc))
return nextc == 0;

这段调用了一个Unsafe的本地CAS方法(CompareAndSet)
使计数器state减1.


第二个核心方法await()是在AbstractQueuedSynchronizer类中doAcquireSharedInterruptibly(int arg)

private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);//把主线程添加到等待队列,下面有详细讲解
boolean failed = true;
try {
for (;;) {//循环等待,阻塞主线程。
final Node p = node.predecessor();//获得节点node的前继
if (p == head) {//确保前继是头节点。
int r = tryAcquireShared(arg);//判断计数器,如果计数器state=0
那么会返回1;
if (r >= 0) {
setHeadAndPropagate(node, r);//唤醒主线程
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}

private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);//一个构造方法,把主线程添加到等待队列,后继是一个共享节点
// 尝试直接把node节点设置为尾节点,
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);//主要是初始化头节点,并且把node设置为尾节点。
return node;
}

private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))//初始化头节点和尾节点
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//尾节点设置为node
t.next = node;//设置node为头节点的后继。注意此时t指向的是head.
return t;
}
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值