Advanced --JUC multi-threaded concurrent programming source to find out the risk of CyclicBarrier

Personal blog navigation page (click on the right link to open a personal blog): Daniel take you on technology stack 

1, the starting point of learning

Baidu translation probably means:

A synchronization aid that allows a set of threads each waits for the arrival of a common barrier point. CyclicBarrier useful thread party program involves a fixed size, these threads party must sometimes wait for each other. This barrier is called cyclic barrier, because it can be re-used after waiting thread is released.

CyclicBarrier supports an optional Runnable command, which is after the last thread involved prescription arrives, but before releasing any thread, run each point barrier once. This barrier helps to update the shared operating state prior to any participant to continue.

Dynamic presentation:

In the above analysis we finished  CountDownLatch source code , it can be understood as a down counter , is based on shared AQS mode of use, and CyclicBarrier compared to CountDownLatch, it is much simpler, which is similar to adding counter , use the source code  ReentrantLock and Condition of combination to use.

2, case presentations CyclicBarrier 

//加法计数器
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        /**
         * 集齐5名队员,开始游戏
         */
        // 开始战斗的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{
            System.out.println("欢迎来到王者荣耀,敌军还有五秒到达战场!全军出击!");
        });
        for (int i = 1; i <=5 ; i++) {
            final int temp = i;
            // lambda能操作到 i 吗
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"第"+temp+"个进入游戏!");
                try {
                    cyclicBarrier.await(); // 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3, starting constructor

//构造器1
/** 创建一个新的CyclicBarrier,它将在给定数量的参与方(线程)等待时触发,并在触发屏障时执行给定的屏障操作,由最后一个进入屏障的线程执行 */   
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

//构造器2
/** 创建一个新的CyclicBarrier,当给定数量的参与方(线程)在等待它时,它将跳闸,并且在屏障跳闸时不执行预定义的操作 */
public CyclicBarrier(int parties) {
        this(parties, null);
    }

1 in which the constructor as the core constructor, where you can specify the  parties  the number of participants of the Council of the game (the number of threads to be blocked) and  barrierAction  task the Council at the end of the game to be executed.

3, starting member variables

   /** 同步操作锁 */
    private final ReentrantLock lock = new ReentrantLock();
    /** 线程拦截器 Condition维护了一个阻塞队列*/
    private final Condition trip = lock.newCondition();
    /** 每次拦截的线程数 */
    private final int parties;
    /* 换代前执行的任务 */
    private final Runnable barrierCommand;
    /** 表示栅栏的当前代 类似代表本局游戏*/
    private Generation generation = new Generation();
    /** 计数器 */
    private int count;
    /** 静态内部类Generation  */
    private static class Generation {
        boolean broken = false;
    }

3, start with the core methods

3.1, source code analysis method [await]

The following analysis of these two methods, respectively [ unscheduled waiting ] and [ timed wait ]!

//非定时等待
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
//定时等待
public int await(long timeout, TimeUnit unit)throws InterruptedException,
              BrokenBarrierException,
              TimeoutException {
       return dowait(true, unit.toNanos(timeout));
   }

You can see, the final two methods are gone [dowait] method, but with different parameters. Here we focus on this approach in the end to see what had been done thing.

//核心等待方法
 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();
            }
            //每次都将计数器的值-1
            int index = --count;
            //计数器的值减为0,则需要唤醒所有线程并转换到下一代
            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();
                }
            }
            //如果计数器不为0 则执行此循环
            // loop until tripped, broken, interrupted, or timed out
            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 {
                        // 若在捕获中断异常前已经完成在栅栏上的等待,则直接调用中断操作
                        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();//最终解锁
        }
    }

Analysis in two steps, firstly the value of down counter is zero, and the counter is not 0, the first first case:

The second case, the counter is not 0, then enter the spin for (;;):

Multi-threaded concurrent access, how to block the current thread?

We look at the source code, here's a look at no time limit [ trip.await ] method:

Await the whole process:

1, is added to the current thread Condition lock queue. In particular, to distinguish between major AQS waiting queue, where incoming FIFO queue is Condition

2, release the lock. Here you can see [fullyRelease] will release the lock, otherwise [acquireQueued (node, savedState)] can not get a lock on another thread deadlock occurs.

3, spin (the while) suspended until awakened CACELLED or out or the like.

4, get a lock [acquireQueued] method, and free yourself from the Condition of the FIFO queue, the surface no longer need to lock yourself (I already have locked)

3.2, Condition queue and wait queue supplement AQS

AQS waiting queue and queue Condition is two separate queues, [await] is released on the basis of the current thread holds the lock on lock resources, and create a new node is added to the Condition Condition tail of the queue, blocking the current thread. [Signal] is the current head node moves to AQS Condition node queue tail, allowed to wait to acquire a lock again. The following drawing demonstrates the difference:

Condition.await execution node 1 () -> (1) will move the head -> (2) and a lock release node waiting queue is removed from the AQS -> (3) a node is added to the waiting queue Condition -> (4) a node is updated lastWrite

Node 2 performs signal () operation -> (1) After firstWrite shift -> (2) the node 4 is removed Condition Queue -> (3) The node 4 is added to the AQS waiting queue to -> (4) Update AQS waiting queue tail

3.3 Summary:

A, Condition data structure:

We know that can be a Condition await (), you will need a FIFO structure in many places these Condition together, then wake up one or more as needed (usually all). So internally Condition need a FIFO queue.
the Node firstWaiter transient Private;
Private lastWaiter the Node transient;

The above described two nodes is a FIFO queue. We previously mentioned combined node (Node) data structure. We found Node.nextWaiter comes in handy! nextWaiter is a series of Condition.await linked together to form a FIFO queue.

Second, when the thread and blocking release

Blocking: await () method, the thread releases the lock after resource, if the node is not AQS wait queue, blocking the current thread acquires the lock if the waiting queue, waiting to try the spin
release: The signal (), node from the condition AQS queue to queue, the process proceeds to the normal procedure of acquiring the lock.

3.4, [signalAll] signalAll source code analysis

[ SignalAll ] method, wake up all the threads in the Condition of blocking queue

private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();//唤醒Condition中等待的线程
    }

public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
     }
/** 这个方法相当于把Condition队列中的所有Node全部取出插入到等待队列中去 */
private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
       }
/** 将节点从条件队列传输到同步队列AQS的等待队列中 */
final boolean transferForSignal(Node node) {
        //核心添加节点到AQS队列方法
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
/** 使用CAS+自旋方式插入节点到等待队列,如果队列为空,则初始化队列 */
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)) {
                    t.next = node;
                    return t;
                }
            }
        }

3.5, source code analysis method [reset]

Finally, we look at how to reset a fence:

The barrier is reset to the initial state. If either party is currently waiting in the wall, they will take BrokenBarrierException return. Note that resetting the interrupt occurs for other reasons can be complex; thread needs to resynchronize otherwise, and choose a way to perform a reset. The best is to create a new barrier for future use

    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

Test reset the code:

First, break the fence, that means all waiting threads (5 waiting threads) will wake up, [await] by [method will throw BrokenBarrierException ] exception return. Then open a new generation, the count is reset and generation, the equivalent of all the return 0.

4 difference, CyclicBarrier with the CountDownLatch

Same point:

1, can achieve a set of threads to wait before reaching a certain condition

2, which has an internal counter when the counter is continually reduced to 0 when all blocked thread will be woken up!

difference:

1, CyclicBarrier counter is controlled by its own, and CountDownLatch counter is controlled by the use of

2, in CyclicBarrier threads invoked await not only put their obstruction, but also the counter by 1, and in CountDownLatch thread calls await method simply blocking themselves without diminishing the value of the counter.

3 In addition, CountDownLatch only one interception, interception and CyclicBarrier cycle can be achieved. In general CyclicBarrier CountDownLatch function can be achieved, but not vice versa.

5. Summary:

When you call [cyclicBarrier.await] method, eventually implemented] [dowait method used to lock ReentrantLock, each speaking counter count value of -1, -1 when the counter value is 0, it will first perform a specified task, Condition of calls [trip.signalAll ()] wake up all the threads and into the next generation

-1 if the current counter value is not 0 when entering the spin, the await execution of Condition [()] method, add the current thread to wait for the condition in the Condition queue, performs call [] [fullyRelease the count value tryRelease] - 1, and then determine whether the count value is 0, 0 will runs the specified tasks, call the Condition of [trip.signalAll ()] wake up all the threads and into the next generation, and then determine whether the AQS waiting queue, if not, then on park the current thread into the AQS wait queue, or spin in the queue waiting to be awakened until the Condition is signalAll into the queue waiting to acquire the lock AQS

Recommended reading:

Attached Java / C / C ++ / machine learning / Algorithms and Data Structures / front-end / Android / Python / programmer reading / single books books Daquan:

(Click on the right to open there in the dry personal blog): Technical dry Flowering
===== >> ① [Java Daniel take you on the road to advanced] << ====
===== >> ② [+ acm algorithm data structure Daniel take you on the road to advanced] << ===
===== >> ③ [database Daniel take you on the road to advanced] << == ===
===== >> ④ [Daniel Web front-end to take you on the road to advanced] << ====
===== >> ⑤ [machine learning python and Daniel take you entry to the Advanced Road] << ====
===== >> ⑥ [architect Daniel take you on the road to advanced] << =====
===== >> ⑦ [C ++ Daniel advanced to take you on the road] << ====
===== >> ⑧ [ios Daniel take you on the road to advanced] << ====
=====> > ⑨ [Web security Daniel take you on the road to advanced] << =====
===== >> ⑩ [Linux operating system and Daniel take you on the road to advanced] << = ====

There is no unearned fruits, hope you young friends, friends want to learn techniques, overcoming all obstacles in the way of the road determined to tie into technology, understand the book, and then knock on the code, understand the principle, and go practice, will It will bring you life, your job, your future a dream.

 

Published 141 original articles · won praise 17 · views 8160

Guess you like

Origin blog.csdn.net/JKX_geek/article/details/102925109