当前位置:网站首页>How to judge that the thread pool has completed all tasks?

How to judge that the thread pool has completed all tasks?

2022-07-05 10:11:00 A bird carved in the desert

A lot of scenarios , We need to wait for all tasks of the thread pool to be executed , Then proceed to the next step . For threads Thread Come on , Well done , Add one more join The solution is solved , However, the judgment of thread pool is more troublesome .

We provide 4 A method to judge whether thread pool tasks have been executed :

  1. Use isTerminated Methods to judge .
  2. Use getCompletedTaskCount Methods to judge .
  3. Use CountDownLatch Judge .
  4. Use CyclicBarrier Judge .

Let's take a look at one by one .

The problem of not judging
If you don't judge whether the thread pool has been executed , The following problems will occur , As shown in the following code :

import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolCompleted {
    public static void main(String[] args) {
        //  Creating a thread pool 
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
                0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
        //  Add tasks 
        addTask(threadPool);
                //  Print the results 
        System.out.println(" Thread pool task execution completed !");
    }
  
    /**
     *  Add tasks to the thread pool 
     */
    private static void addTask(ThreadPoolExecutor threadPool) {
        //  Total tasks 
        final int taskCount = 5;
        //  Add tasks 
        for (int i = 0; i < taskCount; i++) {
            final int finalI = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        //  Random sleep  0-4s
                        int sleepTime = new Random().nextInt(5);
                        TimeUnit.SECONDS.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(String.format(" Mission %d Execution completed ", finalI));
                }
            });
        }
    }
}

The results of the above procedures are as follows :

As can be seen from the above execution results , The program prints first “ Thread pool task execution completed !”, Then it is still executing the task of thread pool in succession , The result of this chaotic execution sequence , Not what we expected . The result we want is when all the tasks are completed , Re print “ Thread pool task execution completed !” Information about .

The reason for the above problem is that the main thread main, And thread pool are executed concurrently , So when the thread pool is not finished ,main The print result code of the thread has been executed . I want to solve this problem , You need to print the results before , First, judge whether the tasks of the thread pool have been fully executed , If not, wait for the task to be executed before printing the results .

Method 1:isTerminated

We can take advantage of the termination state of the thread pool (TERMINATED) To determine whether all the tasks of the thread pool have been executed , But I want the state of the thread pool to change , We need to call the thread pool shutdown Method , Otherwise, the thread pool will always be in RUNNING Running state , Then there is no way to use the termination status to judge whether the task has been completely executed , Its implementation code is as follows :

import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 *  Thread pool task execution completion judgment 
 */
public class ThreadPoolCompleted {
    public static void main(String[] args) {
        // 1. Creating a thread pool 
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
                0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
        // 2. Add tasks 
        addTask(threadPool);
        // 3. Judge whether the thread pool has been executed 
        isCompleted(threadPool); // 【 Core call method 】
        // 4. Thread pool finished executing 
        System.out.println();
        System.out.println(" Thread pool task execution completed !");
    }

    /**
     *  Method 1:isTerminated  Realization way 
     *  Judge whether all tasks of the thread pool have been executed 
     */
    private static void isCompleted(ThreadPoolExecutor threadPool) {
        threadPool.shutdown();
        while (!threadPool.isTerminated()) { //  If it is not finished, it will continue to loop 
        }
    }

    /**
     *  Add tasks to the thread pool 
     */
    private static void addTask(ThreadPoolExecutor threadPool) {
        //  Total tasks 
        final int taskCount = 5;
        //  Add tasks 
        for (int i = 0; i < taskCount; i++) {
            final int finalI = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        //  Random sleep  0-4s
                        int sleepTime = new Random().nextInt(5);
                        TimeUnit.SECONDS.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(String.format(" Mission %d Execution completed ", finalI));
                }
            });
        }
    }
}

Method statement :shutdown The method is to start the orderly shutdown of the thread pool , It will complete all the submitted tasks before it is completely closed , And will not accept any new tasks . When all the tasks in the thread pool have been executed , The thread pool enters the termination state , call isTerminated Method returns the result that true 了 .

The results of the above procedures are as follows :

Defect analysis :
Need to close thread pool .

Expand : All States of the thread pool
The thread pool contains the following 5 States :

RUNNING: Running state .
SHUTDOWN: The closed position .
STOP: Blocking state .
TIDYING: Sorting status .
TERMINATED: Termination status .

If you don't call the thread pool shutdown method , Then the thread pool will always be in RUNNING Running state .

Method 2:getCompletedTaskCount

We can judge the number of planned tasks and completed tasks in the thread pool , To determine whether the thread pool has been fully executed , If the number of tasks planned to be executed = Number of tasks completed , Then all the tasks of the thread pool will be executed , Otherwise, it will not be completed , The specific implementation code is as follows :

/**
 *  Method 2:getCompletedTaskCount  Realization way 
 *  Judge whether all tasks of the thread pool have been executed 
 */
private static void isCompletedByTaskCount(ThreadPoolExecutor threadPool) {
    while (threadPool.getTaskCount() != threadPool.getCompletedTaskCount()) {
    }
}

The results of the above procedures are as follows :

Method statement

getTaskCount(): Returns the total number of scheduled tasks . Because the state of tasks and threads may change dynamically during the calculation process , So the value returned is just an approximation .
getCompletedTaskCount(): Returns the total number of tasks completed . Because the state of tasks and threads may change dynamically during calculation , So the returned value is only an approximation , But it doesn't decrease in successive calls .
Analysis of advantages and disadvantages
The advantage of this implementation is that there is no need to close the thread pool . Its disadvantages are getTaskCount() and getCompletedTaskCount() What is returned is an approximate value , Because the tasks in the thread pool and the state of threads may change dynamically during the calculation process , So both of them return an approximate value .

Method 3:CountDownLatch

CountDownLatch It can be understood as a counter , We created an include N Counters for tasks , Counter after each task is executed -1, Until the counter goes down to 0 when , It means that all tasks have been performed , Then you can execute the code of the next business , Its implementation process and specific implementation code are as follows :

public static void main(String[] args) throws InterruptedException {
    //  Creating a thread pool 
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
        0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
    final int taskCount = 5;    //  Total tasks 
    //  Single counter 
    CountDownLatch countDownLatch = new CountDownLatch(taskCount); // ①
    //  Add tasks 
    for (int i = 0; i < taskCount; i++) {
        final int finalI = i;
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    //  Random sleep  0-4s
                    int sleepTime = new Random().nextInt(5);
                    TimeUnit.SECONDS.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format(" Mission %d Execution completed ", finalI));
                //  The thread is finished , Counter  -1
                countDownLatch.countDown();  // ②
            }
        });
    }
    //  Blocking waits for the thread pool task to finish executing 
    countDownLatch.await();  // ③
    //  Thread pool finished executing 
    System.out.println();
    System.out.println(" Thread pool task execution completed !");
}

Code instructions : The above code is marked as ①、②、③ The line of code is the core implementation code , among : ① Is a declaration that contains 5 Counters for tasks ; ② Is the counter after each task is executed -1; ③ Is the blocking wait counter CountDownLatch Reduced to 0, Indicates that the tasks have been completed , It can be executed await The business code behind the method .

The results of the above procedures are as follows :

Analysis of advantages and disadvantages

CountDownLatch The writing is elegant , And there is no need to close the thread pool , But its disadvantage is that it can only be used once ,CountDownLatch Cannot be reused after being created , in other words CountDownLatch It can be understood as a counter that can only be used once .

Method 4:CyclicBarrier

CyclicBarrier and CountDownLatch similar , It can be understood as a reusable loop counter ,CyclicBarrier You can call reset Method resets itself to the initial state ,CyclicBarrier The specific implementation code is as follows :

public static void main(String[] args) throws InterruptedException {
    //  Creating a thread pool 
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
        0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
    final int taskCount = 5;    //  Total tasks 
    //  Cycle counter  ①
    CyclicBarrier cyclicBarrier = new CyclicBarrier(taskCount, new Runnable() {
        @Override
        public void run() {
            //  Thread pool finished executing 
            System.out.println();
            System.out.println(" All tasks of thread pool have been executed !");
        }
    });
    //  Add tasks 
    for (int i = 0; i < taskCount; i++) {
        final int finalI = i;
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    //  Random sleep  0-4s
                    int sleepTime = new Random().nextInt(5);
                    TimeUnit.SECONDS.sleep(sleepTime);
                    System.out.println(String.format(" Mission %d Execution completed ", finalI));
                    //  The thread is finished 
                    cyclicBarrier.await(); // ②
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

The results of the above procedures are as follows :

Method statement

CyclicBarrier Yes 3 An important way :

Construction method : The constructor can pass two parameters , Parameters 1 Is the number of counters parties, Parameters 2 Yes, the counter is 0 when , That is, the event that can be executed after all tasks are executed ( Method ).
await Method : stay CyclicBarrier Blocking and waiting on , When this method is called CyclicBarrier Your internal counter will -1, Until one of the following happens :
stay CyclicBarrier The number of threads waiting on the parties, That is, the declared number of counters , All threads are released , Carry on .
The current thread is interrupted , Throw out InterruptedException abnormal , And stop waiting , Carry on .
Other waiting threads are interrupted , The current thread throws BrokenBarrierException abnormal , And stop waiting , Carry on .
Other waiting threads time out , The current thread throws BrokenBarrierException abnormal , And stop waiting , Carry on .
Other thread calls CyclicBarrier.reset() Method , The current thread throws BrokenBarrierException abnormal , And stop waiting , Carry on .
reset Method : bring CyclicBarrier Return to the initial state , Intuitively, it does two things :
If there are waiting threads , It will be thrown out. BrokenBarrierException abnormal , And these threads stop waiting , Carry on .
Will the mark be damaged or not broken Set as false.
Analysis of advantages and disadvantages
CyclicBarrier The complexity from design to use is higher than CountDownLatch, Compared with CountDownLatch Its advantage is that it can be reused ( Just call reset You can return to your original state ), The disadvantage is that it is difficult to use .

summary

We provide 4 A method to judge whether thread pool tasks have been executed :

1、 Use isTerminated Methods to judge : By judging the completion status of the thread pool , Need to close thread pool , Generally not recommended .
2、 Use getCompletedTaskCount Methods to judge : The total number of tasks executed and completed through the plan , To determine whether the tasks of the thread pool have been fully executed , If they are equal, it is determined that all execution is completed . But because thread individuals and states change , So what you get is a rough value , May not be accurate .
3、 Use CountDownLatch Judge : Equivalent to a thread safe single counter , It's easy to use , And there is no need to close the thread pool , It is a common judgment method .
4、 Use CyclicBarrier Judge : Equivalent to a thread safe repeat counter , But the use is more complex , Therefore, it is less used in daily projects .

原网站

版权声明
本文为[A bird carved in the desert]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050941163172.html