Java Concurrency: concurrency tool class -- CountDownLatch

Keywords: Java

CountDownLatch allows one or more threads to wait for other threads to complete the operation.

CountDownLatch's constructor takes an int type parameter as a counter. If you want to wait for N points to complete, pass in N here.

When we call the countDown method of CountDownLatch once, N will be reduced by 1, and the await of CountDownLatch will block the current thread until N becomes zero.

Since the countDown method can be used anywhere, the n points mentioned here can be n threads or N execution steps in a thread.

I. application examples

// The boss enters the meeting room and waits for all 5 people to arrive before the meeting. So here are two threads: the owner waits for the meeting thread and the employee to arrive at the meeting room:
class CountDownLatchTest {
    private static CountDownLatch countDownLatch = new CountDownLatch(5);

    // Boss Thread, waiting for employees to arrive at the meeting
    static class BossThread extends Thread {
        @Override
        public void run() {
            System.out.println("Boss Wait in the conference room. There are" + countDownLatch.getCount() + "Personal meeting...");
            try {
                countDownLatch.await(); // Boss wait for
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Everyone's here. Let's have a meeting...");
        }
    }

    // Staff arrive at the meeting room
    static class EmpleoyeeThread extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ",Arrive at the meeting room...."); // Staff arrive at the meeting room count - 1
            countDownLatch.countDown();
        }
    }

    public static void main(String[] args) {
        // Boss Thread start
        new BossThread().start();

        // Staff arrive at the meeting room
        for (int i = 0; i < countDownLatch.getCount(); i++) {
            new EmpleoyeeThread().start();
        }
    }
}

II. Class structure

public class CountDownLatch {
    private final Sync sync; // lock
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        
        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;
            }
        }
    }
}

III. principle analysis

CountDownLatch countDownLatch = new CountDownLatch(5);

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

    /**
     * CountDownLatch.Sync.Sync(int)
     * AQS The state of is used as the count count
     */
    Sync(int count) {
        setState(count);
    }

 

countDownLatch.await();

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    /**
     * AbstractQueuedSynchronizer.acquireSharedInterruptibly(int)
     * Attempt to acquire lock, unable to acquire lock, currently in synchronization queue and suspended
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0) // Attempt to acquire lock
            doAcquireSharedInterruptibly(arg); // Unable to acquire lock, currently in synchronization queue and pending
    }
    
    /**
     * CountDownLatch.Sync.tryAcquireShared(int)
     * state/count Don't allow lock until it's reduced to 0
     */
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

countDownLatch.countDown();

    public void countDown() {
        sync.releaseShared(1);
    }
    
    /**
     * AbstractQueuedSynchronizer.releaseShared(int)
     */
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) { // Attempt to release lock
            doReleaseShared(); // After releasing the lock, wake up the thread of the synchronization queue(call await()Threads)
            return true;
        }
        return false;
    }
    
    /**
     * CountDownLatch.Sync.tryReleaseShared(int)
     * countDown count/state minus one at a time
     * Until count/state is reduced to 0, return true, allowing threads in the synchronization queue to be released
     */
    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;
        }
    }

IV. differences between CyclicBarrier and CountDownLatch

  • Counters for CountDownLatch can only be used once. The counter of the CyclicBarrier can be reset using the reset() method. So CyclicBarrier can handle more complex business scenarios. For example, if there is a calculation error, it can reset the counter and let the threads 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 whether the blocked thread is interrupted. For example, the following code will return true after execution.

 

 

Concurrent tool class (I) CountDownLatch waiting for multithreading to complete

[crash Java concurrency] - J.U.C concurrency tool class: CountDownLatch

Posted by katarra on Sat, 07 Dec 2019 03:53:50 -0800