CountDownLatch

A CountDownLatch is a door that remains closed until the latch reaches the end state and no thread can pass through. When the end state is reached, the door opens and allows all threads to pass through. When the lock reaches the end state, the state will not change and the door will remain open forever

How CountDownLatch is implemented

CountDownLatch implements methods through the internal class Sync, which inherits methods from the AQS rewrite template. Sync internal definition:

private static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 4982264981922014374L; Sync(int count) { setState(count); } /** * get synchronization status */ int getCount() {return getState(); } / / protected int tryAcquireShared(int acquires) {return (getState() == 0)? 1:1; } /** * Releases synchronization */ 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)) return nextc == 0; }}}Copy the code

Sync in CountDownLatch is in shared mode, as you can see from the methods rewritten in the source code. CountDownLatch example:

public class TestHarness { public static long timeTasks(int nThreads, final Runnable task) throws InterruptedException { final CountDownLatch startGate = new CountDownLatch(1); final CountDownLatch endGate = new CountDownLatch(nThreads); for (int i = 0; i < nThreads; i++) { Thread t = new Thread() { @Override public void run() { try { startGate.await(); Try {system.out.println (thread.currentThread ().getName() + "start executing "); task.run(); } finally { endGate.countDown(); System.out.println(thread.currentThread ().getName() + "end of execution "); } } catch (InterruptedException e) { e.printStackTrace(); }}}; t.start(); } long start = System.nanoTime(); startGate.countDown(); endGate.await(); long end = System.nanoTime(); System.out.println(" end-start) + (end-start)); return end - start; } public static void main(String[] args) throws InterruptedException { System.out.println(timeTasks(10, New Runnable() {@override public void run() {system.out.println (thread.currentThread ().getName() + "————————work"); }})); }}Copy the code

Running results:

Thread-0 start Thread-3 Start thread0 ————————work thread0 End Thread-1 start thread-2 Start thread7 Start thread7 ————————work Thread-7 End Thread-9 Start Thread-9————————work thread-9 End Thread-8 Start Thread-8————————work thread-8 No further action is required Thread-2————————work thread-2 End Thread-6 Start Thread-1————————work thread-6 ————————work thread-6 End Thread-5 The operation starts Thread-5————————work thread-5 End Thread-3————————work thread-3 End Thread-4 Start Thread-1 End thread-4 ————————work Thread-4 End The execution of all threads is complete, and the time is 2794976 2794976Copy the code

CyclicBarrier

In contrast to CountDownLatch, which is a one-time object that cannot be reset once it enters the terminated state, CyclicBarrier can be used repeatedly. CyclicBarrier similar to closure, and locking the key difference is that locking is used to wait for events, fences to wait for other threads, its role is to make a set of threads reached a barrier (when also can call sync point) is blocked, until the last thread reaches the barrier, barrier will open the door, all threads that are blocked by the barrier will continue to run.

CyclicBarrier implementation principle

CyclicBarrier constructor


    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;
    }
Copy the code

The parties parameter specifies the number of threads blocked by the fence. The barrierAction parameter specifies the priority thread to execute when all of these threads reach the fence

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; // Get lock lock.lock(); try { final Generation g = generation; If (g.broken) throw new BrokenBarrierException(); // If (g.broken) throw new BrokenBarrierException(); CyclicBarrier if (thread.interrupted ()) {breakBarrier(); CyclicBarrier if (thread.interrupted ()) {breakBarrier(); throw new InterruptedException(); } int index = --count; If (index == 0) {// tripped Boolean ranAction = false; Final Runnable command = barrierCommand; final Runnable command = barrierCommand; if (command ! = null) command.run(); ranAction = true; NextGeneration (); return 0; } finally {CyclicBarrier if (! ranAction) breakBarrier(); }} // Loop all threads to the fence or fence break or thread interrupt or timeout for (;;) {try {// wait for if (! timed) trip.await(); Else if (nanos > 0L) nanos = trip.awaitnanos (nanos); } catch (InterruptedException ie) {CyclicBarrier if (g == generation &&! g.broken) { breakBarrier(); throw ie; } else { // We're about to finish waiting even if we had not // been interrupted, so this interrupt is deemed to // "belong" to subsequent execution. Thread.currentThread().interrupt(); } } if (g.broken) throw new BrokenBarrierException(); if (g ! = generation) return index; CyclicBarrier if (timed && nanos <= 0L) {breakBarrier(); CyclicBarrier if (timed && nanos <= 0L) {breakBarrier(); throw new TimeoutException(); }}} finally {// unlock lock.unlock(); }}Copy the code

Its main logic: if any thread does not reach the fence position, the thread that reaches the fence position will wait until the following scenario occurs: ①. All threads have reached the fence position (2). A thread is interrupted (3). A thread calls the reset() method to disconnect the current fence and reset the fence to its initial state:

public void reset() { final ReentrantLock lock = this.lock; lock.lock(); Try {// breakBarrier(); // Start generation nextGeneration(); // start a new generation } finally { lock.unlock(); }}Copy the code

CyclicBarrier sample

public class CyclicBarrierTest { private static CyclicBarrier cyclicBarrier; Static class CyclicBarrierThread extends Thread{public void run() {system.out.println (" CyclicBarrierThread extends Thread{public void run() {system.out.println (" "+ thread.currentThread ().getName() +"; try { cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args){ cyclicBarrier = new CyclicBarrier(5, New Runnable() {@override public void run() {system.out.println (); }}); for(int i = 0 ; i < 5 ; i++){ new CyclicBarrierThread().start(); }}}Copy the code

Running results:

Athletes: ThREAD-0 Present Athletes: Thread-1 Present Athletes: ThREAD-2 Present Athletes: ThREAD-3 Present Athletes: Thread-4 Present athletes are all present and the competition beginsCopy the code

CountDownLatch is different from CyclicBarrier

① The CountDownLatch counter can only be used once, while the CyclicBarrier counter can be reset using the reset() method. So CyclicBarrier can handle more complex business scenarios. For example, if a calculation error occurs, you can reset the counter and have the thread execute again

CyclicBarrier also provides other useful methods, such as the getNumberWaiting method to get the number of threads blocked by CyclicBarrier. The isBroken() method is used to know if a blocking thread is interrupted

③ Countdownlatches tend to be more than one thread and cyclicbarriers tend to be more than one thread waiting on each other