Java multi-threading series - CountDownLatch Detailed

About CountDownLatch

CountDownLatch is a synchronization aid, until a set of operations is performed in other threads, which allows one or more threads to wait.

CountDownLatch function list

// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
 public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
  }

// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
 public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
  }

// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程
 public void countDown() {
        sync.releaseShared(1);
  }

// 返回当前计数。
 public long getCount() {
        return sync.getCount();
 }

// 返回标识此锁存器及其状态的字符串。
public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
}

CountDownLatch data structure

CountDownLatch data structure is very simple, it is through the " shared lock " to achieve. It contains objects sync, sync is Sync type. Sync is an instance of class that inherits from AQS.

 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) {
            // 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;
            }
        }
    }

    private final Sync sync;

CountDownLatch source code analysis

1. CountDownLatch(int count)

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

Description : This function is to create a Sync object, Sync is the successor to the AQS class. Sync constructor as follows:

Sync(int count) {
     setState(count);
}

the setState () AQS implemented in the source code are as follows:

 /**
     * The synchronization state.
     */
    private volatile int state;

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

Description : In the AQS, state the object is a private volatile int type. For CountDownLatch, state represented "lock counter." GetCount CountDownLatch in () is the final call AQS in getState (), state returned object, namely "lock counter."

2. await()

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

Description : This function is actually called the AQS of acquireSharedInterruptibly (1);

 /**
     * Acquires in shared mode, aborting if interrupted.  Implemented
     * by first checking interrupt status, then invoking at least once
     * {@link #tryAcquireShared}, returning on success.  Otherwise the
     * thread is queued, possibly repeatedly blocking and unblocking,
     * invoking {@link #tryAcquireShared} until success or the thread
     * is interrupted.
     * @param arg the acquire argument.
     * This value is conveyed to {@link #tryAcquireShared} but is
     * otherwise uninterpreted and can represent anything
     * you like.
     * @throws InterruptedException if the current thread is interrupted
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

Description : The role of acquireSharedInterruptibly () is to acquire a shared lock.
If the current thread is interrupted state, an exception is thrown InterruptedException. Otherwise, the call tryAcquireShared (arg) attempts to acquire a shared lock; the attempt is successful is returned, otherwise it calls doAcquireSharedInterruptibly (). doAcquireSharedInterruptibly () causes the current thread to wait until the current thread to acquire a shared lock (or interrupted) before returning.

tryAcquireShared () are rewritten in CountDownLatch.java, its source is as follows:

 protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

Description : The role of tryAcquireShared () is trying to acquire a shared lock.
If the "lock counter = 0", i.e., the lock state is available, it returns 1; otherwise, the lock state is unavailable, or -1.

3. countDown()

public void countDown() {
        sync.releaseShared(1);
    }

Description : This function actually calls the AQS releaseShared (1) release the shared lock.


    /**
     * Releases in shared mode.  Implemented by unblocking one or more
     * threads if {@link #tryReleaseShared} returns true.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryReleaseShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     * @return the value returned from {@link #tryReleaseShared}
     */
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

Description : The purpose releaseShared () is to release the current thread it holds a shared lock.
It will first pass tryReleaseShared () to try to release the shared lock. Attempt is successful, then directly back; the attempt fails, then by doReleaseShared () to release the shared lock.

tryReleaseShared () is rewritten in CountDownLatch.java, the source code is as follows:

 protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                // 获取锁计数器状态
                int c = getState();
                if (c == 0)
                    return false;
                // 将锁计数器减1
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

Description : role tryReleaseShared () is a shared lock is released, the "counter lock" the value -1.

Summary: CountDownLatch through "shared lock" to achieve. When you create CountDownLatch in, will pass a parameter of type int count, this parameter is "locked counter" in the initial state, indicating that the "shared lock" can count up to get to the thread at the same time. When a thread calls the object's CountDownLatch await () method, the thread will wait for "shared lock" is available, in order to obtain "shared lock" and then continue to run. The term "shared lock" available is the "counter lock" is 0! And the initial "counter lock" count value, each time a thread calls countDown CountDownLatch the object () method, before the "counter lock" -1; in this way, there must be a thread calls countDown count () after "lock counter" was zero, while waiting for the aforementioned thread can continue to run!

CountDownLatch use

public class Test {


    private static CountDownLatch countDownLatch;

    public static void main(String[] args) {
        countDownLatch = new CountDownLatch(5);
        try {
            System.out.println("=======主线程开始========");
            for (int i = 0; i < 5; i++) {
                new MyThread().start();
                // 线程等待
            }
            countDownLatch.await();
            System.out.println("=======主线程结束========");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class MyThread extends Thread {

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + " sleep 1000ms.");
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
=======主线程开始========
Thread-0 sleep 1000ms.
Thread-1 sleep 1000ms.
Thread-2 sleep 1000ms.
Thread-3 sleep 1000ms.
Thread-4 sleep 1000ms.
=======主线程结束========

 

Published 35 original articles · won praise 22 · views 946

Guess you like

Origin blog.csdn.net/weixin_38982591/article/details/104014638