CountDownLatch concurrent source code analysis tools and the use of scenarios

Fanger Wei code or search public micro-channel number of the next scan 菜鸟飞呀飞, you can focus on micro-channel public number, read more Spring源码分析, and Java并发编程articles.

Micro-channel public number

Brief introduction

  • CountDownLatch is a utility class provided under JUC package, its role is to make one or a group of threads waiting for the other thread execution is completed, then perform them again. Can guess from the name, it is by counting backwards, and finally open the door the lock, that is, each thread after finishing his work, so that the counter is decremented once, when the counter decrements to 0, the lock (latch) is opened the main thread (or waiting thread) then perform their work.
  • In practice, we may have a need to use multiple threads to deal with the problem, it is also necessary to control the order of execution of these threads, this time we can choose to use the Thread class provided in the join () method, you can use today is about CountDownLatch introduced to solve, may also be used in another class CyclicBarrier JUC package to solve.

how to use

  • CountDownLatch's very simple to use, it has only a constructor, the constructor need to pass a parameter of type int, this parameter is used to control the number of times before descending CountDownLatch need to release the lock (open door). CountDownLatch also provides the following three methods, detailed information in the table below.
Method name Methods role
void await() Let calling thread to block this method, when CountDownLatch counter reduced to zero, the thread will allow unblocking
boolean await(long timeout, TimeUnit unit) Let thread calls this method overtime blocking, if more than a specified time, CountDownLatch the counter has not reduced to 0, then the thread will return directly
void countDown() Let CountDownLatch counter minus 1, when the counter value is reduced to zero, let CountDownLatch blocked thread unblocking
  • Below a simple scene, a brief introduction CountDownLatch usage. As a student, there are always a variety of exams, each exam, subject teachers will be marking calculate the total score, total score ranking. In this process, the subjects of marking is at the same time, since each subject teacher's scoring rate is not the same, so the overall score and total score ranking people need to wait until after the completion of all the teachers marking. This time we can simulate this scenario in the program with CountDownLatch this tool class. The teacher of each subject as a thread, since each subject teacher's scoring rate is not the same, so a random thread to sleep for some time, when the teacher marking the completion of each subject, it calls countDown CountDownLatch's () method to make counter minus one, call CountDownLatch in the main thread await () method, the purpose to let the main thread wait for the teacher marking the completion of all, when all teachers to complete the scoring counter decrements to zero, and from the main thread will await () method at unblocking, and then add the total score, ranking and so on. Demo example is as follows.
public class CountDownLatchDemo {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        List<String> teachers = Arrays.asList("语文老师","数学老师","英语老师","物理老师","化学老师","生物老师");
        Random random = new Random();
        // 创建6个线程,模拟6个科目的老师同时开始阅卷
        List<Thread> threads = new ArrayList<>(6);
        for (int i = 0; i < 6; i++) {
            threads.add(new Thread(()->{
                try {
                    int workTime = random.nextInt(6) + 1;
                    // 让线程睡眠一段时间,模拟老师的阅卷时间
                    Thread.sleep(workTime * 1000l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "阅卷完成");
                // 每位老师阅卷完成后,就让计数器减1
                countDownLatch.countDown();
            },teachers.get(i)));
        }
        for (Thread thread : threads) {
            thread.start();
        }
        // 让主线程等待所有老师阅卷完成后,再开始计算总分,进行排名
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有科目的老师均已阅卷完成");
        System.out.println("开始计算总分,然后排名");
    }
}
复制代码

Source code analysis

  • Understand the use of CountDownLatch, then look at works of CountDownLatch.
  • CountDownLatch actually a shared lock, its underlying queue synchronizer using AQS achieved. CountDownLatch specifies the initial value of the counter in the constructor, i.e. the value of the state variable of the AQS. At the same time it allows multiple threads to access state, called once every time the countDown () method, let the value of minus one state; When calling the await () method will be determined at this time whether the state value is 0, if 0, let returns the current thread, if you do not enter the sync queue is 0, let the current thread to wait.
  • Since CountDownLatch implemented using AQS, so it need to define an internal synchronization assembly need to inherit AQS (this practice is common practice AQS series lock), defines an inner class CountDownLatch in the Sync, Sync inherited AQS, and rewrite the tryAcquireShared (), tryReleaseShared () method of AQS.
  • When using the constructor Create CountDownLatch CountDownLatch in constructor will instantiate assembly Sync and Sync are by constructor parameters, values ​​for the state variable initialization AQS synchronization, the value of the size of the constructor is passed in a CountDownLatch int type values, used to represent the size of the counter.
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}
复制代码
private static final class Sync extends AbstractQueuedSynchronizer {

    Sync(int count) {
        setState(count);
    }
}
复制代码
  • When calling a CountDownLatch the countDown () method calls to sync.releaseShared (1), i.e., the AQS releaseShared () method. ReleaseShared source () method is as follows.
public final boolean releaseShared(int arg) {
    // 尝试释放共享锁
    if (tryReleaseShared(arg)) {
        /**
         * 当释放锁完成后,同步状态state=0,此时说明后面的线程可以获取锁了
         * 如果此时同步队列中有人的等待,就唤醒后面的线程
         * 如果无人等待,就将首节点的waitStatus设置为-3,表示同步状态可以无条件的传播下去,即后面的线程都可以直接获取锁了
         */
        doReleaseShared();
        return true;
    }
    return false;
}
复制代码
  • In releaseShared () method, will first call tryReleaseShared () method in the subclass, this will call into CountDownLatch the inner class Sync's tryReleaseShared () method. Source follows.
protected boolean tryReleaseShared(int releases) {
    // Decrement count; signal when transition to zero
    // 将同步状态进行减一,减一之后,同步状态变为0,就返回true,表示可以唤醒同步队列中正在等待的线程了
    for (;;) {
        int c = getState();
        // 在对state进行减一操作之前,会先判断一下state的值是否为0,如果state已经为0了,此时还有线程来对state进行减1,这个时候是不正常的操作,因此会返回false
        if (c == 0)
            return false;
        int nextc = c-1;
        // 利用CAS操作来设置state的值
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
复制代码
  • Role tryReleaseShared () method is to let the value of the state of minus 1, if the value of state after a cut to 0, returns true, indicating that this time can wake up thread synchronization queue are waiting, if not zero, returns false. When the tryReleaseShared () method, it will return to the releaseShared AQS () method, if tryReleaseShared () method returns false, then releaseShared () returns directly to the end; if tryReleaseShared () method returns true, so then executes doReleaseShared () method. doReleaseShared () method is a method AQS template defined, shared lock is used to handle logic, its main role is to wake up the thread synchronization queue waiting.
  • When the call await CountDownLatch () will call the sync.acquireSharedInterruptibly (1), i.e., the AQS acquireSharedInterruptibly () method. Source acquireSharedInterruptibly () method is as follows.
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    // 响应中断
    if (Thread.interrupted())
        throw new InterruptedException();
    // tryAcquireShared()方法是尝试获取锁
    // 对于CountDownLatch而言,当state=0时,会返回1,这表示锁被所有的线程都释放了,当state不等于0时,会返回-1,表示还有线程持有锁
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
复制代码
  • In acquireSharedInterruptibly () method will first call tryAcquireShared subclass () method, where the call is an internal class Sync CountDownLatch of tryAcquireShared () method. Logic tryAcquireShared () method is very simple, it is determined whether the state value is 0, if it is 0, it returns 1, not 0, -1. When the state is zero, is the value of the counter is reduced to zero, indicating that all threads have completed their work, so this time tryAcquireShared () method returns 1, then when back in AQS, acquireSharedInterruptibly () method on It will direct the end of the current thread does not block. If the state is not zero, it shows the value of the counter has not been reduced to zero, there is no thread to perform their work done, without calling countDown () method, so this time tryAcquireShared () method returns -1, then back to the AQS when, acquireSharedInterruptibly () method is not directly over, but then performed doAcquireSharedInterruptibly () method. doAcquireSharedInterruptibly () method is a method AQS template, the main effect of this approach is the processing logic associated shared lock, if a shared lock acquisition has failed, let the thread into the synchronization queue park. Here, if the value of the counter is not 0, then the current thread calls await () method after, will enter the synchronization queue park, until some other thread calls countDown () method to counter reduced to zero, only the current thread wakes, or the current thread is interrupted.
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
复制代码
  • Similar implement logic and await () method await (long timeout, TimeUnit unit) method, but adds a timeout judgment based await () methods, interested friends can be studied on their own. Overall speaking, CountDownLatch principle is relatively simple, and the principle of shared locks to achieve the same, but tryAcquireShared () method and tryReleaseShared () method of logical slightly change.

to sum up

  • This paper describes the role of CountDownLatch, it can make a thread or set of threads waiting for the other thread execution is completed, their only run, the purpose of the order of thread execution control can be achieved. By scoring a then calculate the total score, ranking case demonstrates CountDownLatch to use. Finally, a brief analysis of the CountDownLatch source implementation of the underlying CountDownLatch is achieved in accordance with the relevant method of sharing the lock of AQS.
  • CountDownLatch in the course should be noted that, it is best to use await(long timeout, TimeUnit unit)to block a thread, because if the child thread processing task has not been performed, would have been not call countDown () method, so that the counter will not be reduced to zero, the main cause thread has been blocked waiting for, can not do anything, it is recommended to use the await (long timeout, TimeUnit unit) . If a particular business, requires that all threads must execute before executing the main thread, then it would use the await () method, but in sub-thread processing tasks of the code, it is best to use try ... catch ... finally statement and then in the finally block countDown () call, it will be very easy to dig their own pit.
  • I have one in use CountDownLatch process, due to the use of that await () method, then there is no use try ... catch ... finally in the child thread, the last because there is a thread error in dealing with business logic, leading to this thread does not call countDown () method, so the main thread has been blocked, been waiting there. Even worse is that, since this is an abnormal child thread, there is no try ... catch ... finally, this time the main thread 吞掉stack anomalies child threads, resulting in what the error log does not print. Since this code will be executed when the service starts, so when the phenomenon is that the service always get up, do not they print the error log. At that time encounter this problem, the code line has been running for a long time, it appeared only in a test environment, so never even to think about this place. Coupled with their multi-threaded relevant knowledge to master degree is almost zero, check for a long time did not find the cause, the server restarts n times, not seeing restart Dafa Hao make, and can only ask colleagues finally found the error. The final solution is to use try ... catch ... finally in the sub-thread, and then call countDown () in the finally block. In fact, because of this problem but still very own food, there is not enough understanding of multi-threaded, it took a long time to solve with jstack command to see if the thread's stack on the server can be found is where a problem. It is also because of this lesson, so I decided to start to learn complicated by the relevant source code.

recommend

Micro-channel public number

Guess you like

Origin juejin.im/post/5dcacebe518825574937df84