Countdownlatch & Cyclicbarrier Source Android version Implementation analysis

Source: Internet
Author: User
Tags finally block

Countdownlatch

Countdownlatch allows one or more threads to wait until other threads have completed a secondary Synchronizer with a series of operations.

initializes the Countdownlatch with a specified count value. The await method blocks until the current count is reduced to 0 because the call to the Countdown method, after which all the waiting threads are freed, and after that the await call will return immediately. This is a one-time behavior--count cannot be reset. If you need a version that can reset count, consider using Cyclicbarrier.
In fact, this kind of implementation is very simple, and similar to Reentrantlock, public methods are called internal class Bridge mode, inner class is inherited AQS lock implementation. Specific as follows:
  Private static final class Sync extends Abstractqueuedsynchronizer {        private static final long Serialversionuid = 4982 264981922014374L;        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;}}    
The constructor calls SetState to set the count value to the current state. Internal class sync is created by the Countdownlatch constructor, and of course non-negative values are also judged here,
    public Countdownlatch (int count) {        if (count < 0) throw new IllegalArgumentException ("Count < 0");        This.sync = new sync (count);    }
also, look at Countdownlatch's await and implementation of the Countdown method:
   public void Countdown () {        sync.releaseshared (1);    }    public void await () throws Interruptedexception {        sync.acquiresharedinterruptibly (1);    }
you can see that the Aqs Acquiresharedinterruptibly attempted to acquire a shared lock, the countdown method called the releaseshared attempt to release the shared lock, the parameters of the two methods are all 1, So when there are multiple threads calling await at the same time, we see the Tryacquireshared method implementation of the internal sync class, because the constructor has called SetState to set the current lock state number to count, so here in GetState () 's judgment will always return the value of count, so tryacquireshared will always return 1, and then the threads that call await will enter the waiting queue.
continue to look at the implementation of the countdown, called the Aqs releaseshared method, after the call came to the internal sync class tryreleaseshared method, obviously see the method just use spin to the current lock state number minus one, Until the number of lock states equals 0, and then returns True, so that Aqs wakes all the threads that have previously entered the waiting queue and waits for a shared lock, and if the subsequent thread calls await, the tryacquireshared will return 1, since the number of lock states has changed to 0. At this point, the function countdown will return immediately, no need to enter the waiting queue.
It is important to note that the count value here has been determined in the constructor, and there is no way to modify it, of course, as determined by the original design intent of the class. If you need to be able to make changes to the count value, you can refer to Cyclicbarrier.

Cyclicbarrier

Cyclicbarrier allows a group of threads to wait for each other until a fair barrier (common barrier point). Unlike Countdownlatch, Cyclicbarrier focuses on waiting with each other and adds a way to reset the original state.

In programs that involve a set of fixed-size threads, these threads have to wait for each other, and cyclicbarrier is useful at this time. Because the barrier (barrier) can be reused after releasing the waiting thread, it is called a circular barrier. Cyclicbarrier supports an optional runnable command that runs only once at each barrier point after the last thread in a set of threads arrives (but before releasing all threads). This barrier operation is useful if you update the shared state before continuing with all participating threads.
If the barrier operation is not dependent on the execution of the thread group when it is hoisted, any thread within the thread group can perform a barrier operation when it is released. To improve this behavior, each await call returns the index of the thread reaching the barrier. You can then choose which thread should perform the barrier operation, for example:
    if (barrier.await () = = 0) {        //Perform barrier operation    }
Cyclicbarrier for failed synchronization attempts, an all-or-nothing destruction model is used (breakage models): If a thread leaves the barrier point prematurely due to a break, an exception, or a timeout, All other threads waiting at the barrier point will leave the barrier point by throwing brokenbarrierexception (or interruptedexception exception, while being interrupted). Next look at the specific implementation.

first look at the constructors and member variables of the Cyclicbarrier class and the inner class generation:
    private static Class Generation {        Boolean broken = false;    }
Cyclicbarrier declares an inner class generation, where each barrier point is used to represent a generation instance. When the barrier points are destroyed or reset, the generation will change.
    Lock    Private Final reentrantlock lock = new Reentrantlock ()    for protection barrier point entry The conditional object that causes the thread to wait in an await until the barrier point is dropped (tripped)    private final Condition trip = Lock.newcondition ();    The number of threads that need to call await to fall off the barrier point, the final variable    private final int parties;    Commands that need to be run after the barrier point has been dropped    private final Runnable Barriercommand;    Current Generation    private Generation Generation = new Generation ();    In each generation descending from parties to 0, count is reset to the private int count when the new generation is created or the barrier points are destroyed    ;
The member variable uses lock and trip for synchronous control, and parties and count record the number of threads, generation represents the current state of the barrier point, and the command to be executed after the Barriercommand records the barrier point destruction.
    public Cyclicbarrier (int parties, Runnable barrieraction) {        if (parties <= 0) throw new IllegalArgumentException () ;        This.parties = parties;        This.count = parties;        This.barriercommand = barrieraction;    }
where parties represents the number of threads that need to invoke await before the barrier is dropped (tripped), it is important to note that the parties is the final variable and therefore cannot be changed, and the member variable count is decremented at each await. When resetting, assign a value to the parties. Barrieraction indicates that the command is executed when the barrier point is dropped off. Note that when the parties number is set in the constructor, it cannot be changed, and if the barrier is dropped, reset can be called, and we first look at the implementation of await. In the implementation, await has two different function versions, including no timeout version and timeout version, as follows.
 No timeout version public int await () throws Interruptedexception, Brokenbarrierexception {try {return dowait        (false, 0L); } catch (TimeoutException toe) {throw new Error (toe);//Cannot happen}}//timeout version public int a               Wait (long timeout, timeunit unit) throws Interruptedexception, Brokenbarrierexception,    timeoutexception {return dowait (true, Unit.tonanos (timeout));               } 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 {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 (); }    }
you can see that an await implementation without a time-out version and a timeout version is just different from the parameter that calls the Dowait function. Look at the realization of dowait in detail. Unlike the previously parsed Reentranlock,reentrantreadwritelock, and the countdownlatch above, Cyclicbarrier does not overload the Aqs class, Instead, it chooses to use Reentrantlock directly and its condition.
the dowait function calls Renntrantlock's Lock method at the beginning to attempt to acquire the lock, and then creates a stack variable, G, to reserve the member variable generation, because generation is later re-assigned by another thread, after the lock is acquired successfully. Therefore, the practice of using stack variable retention in multi-threaded synchronization is a practical skill. Then examine the current generation's broken variable, if the variable is true (in the event of a reset or if there is enough thread to call the class await to break the barrier point), then immediately throws the Brokenbarrierexception exception, and then checks whether the current thread has been interrupted , if the interrupt is called Breakbarrier, according to the characteristics of cyclicbarrier, when an await thread breaks, then the barrier point is destroyed, then all the await threads are awakened to throw an exception.
    private void Breakbarrier () {        Generation.broken = true;        Count = parties;        Trip.signalall ();    }
Breakbarrier function is to destroy the barrier point, The function first turns the generation.broken of the member variable to TRUE, resets the count to the parties value, and then calls the conditional object trip's Signalall to wake up all the threads waiting in await. As we will see in the await, Breakbarrier will throw an exception on all threads that were previously in await. If the thread is not interrupted, count is reduced and retained to index (count will also be modified by other threads when the lock is released later), if index is 0, the barrier point has been destroyed, and if Barriercommand is not NULL, the command is executed, If the execution succeeds Ranaction is modified to true, or if any exception is thrown in the command, the finally block is called Breakbarrier destroys the barrier point and wakes up the other thread. When the command executes successfully, the nextgeneration is called (shedding the current barrier point):
    private void Nextgeneration () {        trip.signalall ();        Count = parties;        Generation = new Generation ();    }
The function wakes up all the threads in the wait, resets count, and then creates a new instance of the Generation class, which is equal to resetting the state inside the cyclicbarrier.
Let's look back at the implementation of Dowait, if the current index has not been decremented to 0, it will go into a loop where the await function of trip is called first to time out or infinite wait, if during the wait, If the thread throws a interruptedexception interrupt the current threads, continue to determine if the generation and current changes, if the stack variable g and generation are equal, and the broken is false, it means that an interrupt occurred in the await , so call Breakbarrier to break the barrier point and throw an exception, but if the above conditions do not match, it indicates that the current barrier point has been dropped, but the thread is still waiting for the wake-up process of the interruption, the interruption should be irrelevant to the current await, You need to call the Thread.Interrupt method to reset the interrupt identity.
and then, if you wake up normally or wait for an await, Also continue to Judge G.broken, if true, it means that the barrier is broken, to throw brokenbarrierexception exception, if the stack variable g is not equal to generation, it means that the current barrier point has been dropped, so to return to the index of the previous index expression into the barrier point. Additionally, if the timeout is exceeded, the breakbarrier is also required and the timeoutexception is thrown. Because of the thread concurrency problem, if the above judgment fails, you must re-cycle. You must call Lock.unlock to release the lock when you finally leave the function.

In addition, you can call the Reset method if the number of threads in await does not reach parties, but resynchronization is required.
    public void Reset () {        final reentrantlock lock = This.lock;        Lock.lock ();        try {            breakbarrier ();   Break the current generation            nextgeneration ();//Start a new generation        } finally {            lock.unlock ();        }    }
the function implementation is simple, try to acquire the lock, and then call the Breakbarrier and Nextgeneration methods. After this call, the barrier points are destroyed (breakage), the thread that was previously in the await is awakened and the exception is thrown, and then the call Nextgeneration resets the current state so that later await can be re-waited again.

Summarize

In this way, we have analyzed the Countdownlatch and the cyclicbarrier in a complete way. Countdownlatch focuses on multiple groups of threads waiting for another set of threads to complete and cannot be reset; Cyclicbarrier focuses on a group of threads waiting for each other to finish the operation, but can be reset.
If you want to take into account why Countdownlatch does not provide a way to reset, personally think that considering the implementation of the reset, it must be like cyclicbarrier to consider the impact on other waiting threads, which will inevitably make the entire Synchronizer model more complex, It makes the user inconvenient, but it also increases the difficulty of implementation, which is not as good as the solution provided by the JUC package, provides countdownlatch for more common simple concurrency, and provides cyclicbarrier to help with more complex concurrency models. In fact, the Countdownlatch synchronization model is simpler than the cyclicbarrier, mainly reflected in the waiting threads do not affect each other, and the implementation of Countdownlatch is more simple than cyclicbarrier.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.