Java Concurrency CountDownLatch tools and principles resolved CyclicBarrier

[TOC]

Java Concurrency CountDownLatch tools and principles resolved CyclicBarrier

I. Introduction

CountDownLatch allows one or more threads waiting for other threads to finish.

CyclicBarrier literally means can be reused (Cyclic) barrier (Barrier). It needs to be done is to let a group of threads reach a barrier (also called synchronization point) is blocked when it until the last thread reaches the barrier, the barrier will open the door, all barriers blocked thread will continue to run.

Second, the code shows

CountDownLatchDemo
public class CountDownLatchDemo {

    public static final CountDownLatch count = new CountDownLatch(10);
    private static int j = 0;

    public static void main(String[] args) throws Exception {

        for (int i = 0; i < 10; i++) {

            new Thread(
            ()-> {
                System.out.println("我是"+(++j));
                count.countDown();
                }
            ).start();

        }
        count.await();
        System.out.println("我是总数"+j+"!!!");
    }

}

运行结果:
我是1
我是2
我是3
我是4
我是5
我是6
我是7
我是8
我是9
我是10
我是总数10!!!
CyclicBarrierDemo
public class CyclicBarrierDemo {

    private static final CyclicBarrier c = new CyclicBarrier(6,new Thread(() ->
        System.out.println("我是最后一个")
            ));

    private static AtomicInteger index = new AtomicInteger(1);

    public static void main(String[] args) throws Exception, BrokenBarrierException {
        for (int i = 1; i <= 6; i ++) {
            new Thread(() -> {
                try {
                    c.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println("我是:"+(index.getAndIncrement()));
            }) .start();
        }
    }

}

运行结果:
我是最后一个
我是:1
我是:2
我是:3
我是:4
我是:5
我是:6

Third, resolve source

Source CountDownLatch

principle:

CountDownLatch also called atresia, CountDownLatch constructor accepts a parameter of type int as a counter, if you want to wait for the completion of n nodes, then the incoming N; when we call countDown method of CountDownLatch, N will be minus 1, CountDownLatch of await this method will block until N becomes 0; since countDown method may be used anywhere, where said N points, N may be threads, a thread may be one which is N steps.

Source:

// 构造方法
public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
   }

// 内部类 Sync 继承AQS
private static final class Sync extends AbstractQueuedSynchronizer {

}
countDown method
    public void countDown() {
        // 调用了AQS的releaseShared方法
        sync.releaseShared(1);
    }
    // 这是Sync的tryReleaseShared 
    // AQS的releaseShared会调用子类的tryReleaseShared 用来控制count
    // tryReleaseShared 共享式的释放状态 具体参考AQS
     protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                // 获取的其实就是我们构造函数的count
                int c = getState();
                // count == 0 证明整个记录流程已经完毕了
                if (c == 0)
                    return false;
                // 减1 
                int nextc = c-1;
                if (compareAndSetState(c, nextc)) cas 更新
                    return nextc == 0; // 等于0,返回ture 证明计数结束了,可以去唤醒同步队列的线程了
                                        // 唤醒是AQS的releaseShared方法
                                        // 结合CountDownLatch的await方法理解整这里 
            }
        }
await method
    public void await() throws InterruptedException {
        // 共享式获取同步转态
        sync.acquireSharedInterruptibly(1);
    }

    // 这是Sync的方法
    // await 其实是调用的AQS的acquireSharedInterruptibly 但是aqs会调用子类tryAcquireShared
    // 我们看到值有state等于0 才会返回true 成功 -1 表示失败 失败就要加入同步队列
    // 所以在countDown方法里面等于0 为什么要去唤醒 ,应为这里会进入同步队列
    protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
    }

By source we can find this method only if countDown count-down is completed, other threads to execute, because the calling thread will await enter AQS synchronization queue, and then blocked.

Source CyclicBarrier

principle:

CyclicBarrier default constructor is CyclicBarrier (int parties), is a barrier to intercept parameter indicates the number of threads, each thread calls await tell CyclicBarrier I have reached the barrier, and then the current thread is blocked; CyclicBarrier sea provides a high-level constructor, CyclicBarrier (int parties, Runnable barrierAction), is used when the thread reaches the barrier, the thread precedence barrierAction, easy to handle more complex business logic.

Source:

public CyclicBarrier(int parties) {
        this(parties, null);
    }

public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    /** The number of parties */
    private final int parties;
    /* The command to run when tripped */
    private final Runnable barrierCommand;
    /** The current generation */
    private Generation generation = new Generation();
await method
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        // 获取锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 这一代的状态
            final Generation g = generation;
            // 默认为false Barrier被Broken 就会为true
            if (g.broken)
                throw new BrokenBarrierException();
            // 线程被中断了,标记为breakBarrier,唤醒所有线程
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            // 计数器减减
            int index = --count;
            // 到达 trip
            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();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            // 一直自旋直到发生:tripped, broken, interrupted, timed out
            for (;;) {
                try {
                    // 带时间
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                    // 自旋过程中发生中断
                } catch (InterruptedException ie) {
                    // 等于说明当前被重点的这个线程没有被broken
                    // 抛异常
                    if (g == generation && ! g.broken) { 
                        breakBarrier();
                        throw ie;
                    } else { // 不等于说明后来的线程已经broken了
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        // 中断线程 breakBarrier已经没有意义了
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken) // 屏蔽Broken
                    throw new BrokenBarrierException();
                // 别的线程更新了generation 不属于当前代
                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }

We found CyclicBarrier together all the threads are blocked until it reaches the barrier point, then wake up to perform all together.

IV Summary

CountDownLatch and CyclicBarrier can achieve a thread waits until one or more threads execute after reaching a point, but this state has one or more threads are not the same, CountDownLatch is a not to execute a block until everyone finished execution in thread calls await method of execution, CyclicBarrier is a blockage to one until we have finished blocking, then execute priority thread inside the constructor, in the wake of all the blocked threads; CountDownLatch counter can only be executed once, CyclicBarrier can perform many times, so CyclicBarrier can perform complex business scenarios.

Reference "Art Java concurrent programming."

Guess you like

Origin blog.51cto.com/14220760/2417547