• 判断线程池任务执行完成的方式


    Thread线程是否执行完成,我们可以调用join方法然后等待线程执行完成;那在使用线程池的时候,我们如何知道线程已经执行完成了?本文就带给大家五种判断的方式:
    ● isTerminated() 方式,在执行 shutdown() ,关闭线程池后,判断是否所有任务已经完成。
    ● ThreadPoolExecutor 的 getCompletedTaskCount() 方法,判断完成任务数和全部任务数是否相等。
    ● CountDownLatch计数器,使用闭锁计数来判断是否全部完成。
    ● 手动维护一个公共计数 ,原理和闭锁类似,就是更加灵活。
    ● 使用submit向线程池提交任务,Future判断任务执行状态。

    方法一:isTerminated()

    测试代码

    package pool;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author 百里
     */
    public class BaiLiIsShutdownThreadPoolDemo {
        /**
         * 创建一个最大线程数15的线程池
         */
        public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                10,
                15,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
        /**
         * 线程执行方法,随机等待0到10秒
         */
        private static void sleepMethod(int index){
            try {
                long sleepTime = new Double(Math.random() * 10000).longValue();
                Thread.sleep(sleepTime);
                System.out.println("当前线程执行结束: " + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 方法一:isTerminated
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> sleepMethod(index));
            }
            pool.shutdown();
            while (!pool.isTerminated()){
                Thread.sleep(1000);
                System.out.println("还没停止。。。");
            }
            System.out.println("全部执行完毕");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    上述代码处理逻辑在主线程中进行循环判断,全部任务是否已经完成。
    这里有两个主要方法:
    ● shutdown() :对线程池进行有序关闭。调用该方法后,线程池将不再接受新的任务,但会继续执行已提交的任务。如果线程池已经处于关闭状态,则对该方法的调用没有额外的作用。
    ● isTerminated() :判断线程池中的所有任务是否在关闭后完成。只有在调用了shutdown()或shutdownNow()方法后,所有任务执行完毕,才会返回true。需要注意的是,在调用shutdown()之前调用isTerminated()方法始终返回false。
    优缺点分析
    优点 :操作简单。
    缺点 :需要关闭线程池。并且日常使用是将线程池注入到Spring容器,然后各个组件中统一用同一个线程池,不能直接关闭线程池。

    方法二:getCompletedTaskCount()

    测试代码

    package pool;
    
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author 百里
     */
    public class BaiLiIsShutdownThreadPoolDemo {
        /**
         * 创建一个最大线程数15的线程池
         */
        public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                10,
                15,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
        /**
         * 线程执行方法,随机等待0到10秒
         */
        private static void sleepMethod(int index){
            try {
                long sleepTime = new Double(Math.random() * 10000).longValue();
                Thread.sleep(sleepTime);
                System.out.println("当前线程执行结束: " + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 方法二:getCompletedTaskCount
         * @param args
         * @throws InterruptedException
         */
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> sleepMethod(index));
            }
            //当线程池完成的线程数等于线程池中的总线程数
            while (!(pool.getTaskCount() == pool.getCompletedTaskCount())) {
                System.out.println("任务总数:" + pool.getTaskCount() + "; 已经完成任务数:" + pool.getCompletedTaskCount());
                Thread.sleep(1000);
                System.out.println("还没停止。。。");
            }
            System.out.println("全部执行完毕");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    上述代码处理逻辑还是一样在主线程循环判断,主要就两个方法:
    ● getTaskCount() :返回计划执行的任务总数。由于任务和线程的状态可能在计算过程中动态变化,返回的值只是一个近似值。这个方法返回的是线程池提交的任务总数,包括已经完成和正在执行中的任务。
    ● getCompletedTaskCount() :返回已经完成执行的任务的大致总数。由于任务和线程的状态可能在计算过程中动态改变,返回的值只是一个近似值,并且在连续的调用中不会减少。这个方法返回的是已经完成执行的任务数量,不包括正在执行中的任务。
    优缺点分析
    ● 优点 :不必关闭线程池,避免了创建和销毁带来的损耗。
    ● 缺点 :使用这种判断存在很大的限制条件;必须确定在循环判断过程中没有新的任务产生。

    方法三:CountDownLatch

    测试代码

    package pool;
    
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author 百里
     */
    public class BaiLiIsShutdownThreadPoolDemo {
        /**
         * 创建一个最大线程数15的线程池
         */
        public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                10,
                15,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
        /**
         * 线程执行方法,随机等待0到10秒
         */
        private static void sleepMethod(int index){
            try {
                long sleepTime = new Double(Math.random() * 10000).longValue();
                Thread.sleep(sleepTime);
                System.out.println("当前线程执行结束: " + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 方法三:CountDownLatch
         * @throws Exception
         */
        public static void main(String[] args) throws Exception {
            //计数器,判断线程是否执行结束
            CountDownLatch taskLatch = new CountDownLatch(10);
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> {
                    sleepMethod(index);
                    taskLatch.countDown();
                    System.out.println("当前计数器数量:" + taskLatch.getCount());
                });
            }
            //当前线程阻塞,等待计数器置为0
            taskLatch.await();
            System.out.println("全部执行完毕");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    优缺点分析
    优点 :代码优雅,不需要对线程池进行操作。
    缺点 :需要提前知道线程数量;性能较差;还需要在线程代码块内加上异常判断,否则在 countDown之前发生异常而没有处理,就会导致主线程永远阻塞在 await。

    方法四:公共计数

    测试代码

    package pool;
    
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author 百里
     */
    public class BaiLiIsShutdownThreadPoolDemo {
        /**
         * 创建一个最大线程数15的线程池
         */
        public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                10,
                15,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
        /**
         * 线程执行方法,随机等待0到10秒
         */
        private static void sleepMethod(int index){
            try {
                long sleepTime = new Double(Math.random() * 10000).longValue();
                Thread.sleep(sleepTime);
                System.out.println("当前线程执行结束: " + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        private static int taskNum = 0; //计数器
    
        /**
         * 方法四:公共计数
         * @throws Exception
         */
        public static void main(String[] args) throws Exception {
            Lock lock = new ReentrantLock();
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> {
                    sleepMethod(index);
                    lock.lock();
                    taskNum++;
                    lock.unlock();
                });
            }
            while(taskNum < 10) {
                Thread.sleep(1000);
                System.out.println("还没停止。。。当前完成任务数:" + taskNum);
            }
            System.out.println("全部执行完毕");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    这种实现其实就是通过加锁计数,然后循环判断。
    优缺点分析
    ● 优点 :手动维护方式更加灵活,对于一些特殊场景可以手动处理。
    ● 缺点 :和CountDownLatch相比,一样需要知道线程数目,但是代码实现比较麻烦。

    方法五:Future

    测试代码

    package pool;
    
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author 百里
     */
    public class BaiLiIsShutdownThreadPoolDemo {
        /**
         * 创建一个最大线程数15的线程池
         */
        public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                10,
                15,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
        /**
         * 线程执行方法,随机等待0到10秒
         */
        private static void sleepMethod(int index){
            try {
                long sleepTime = new Double(Math.random() * 10000).longValue();
                Thread.sleep(sleepTime);
                System.out.println("当前线程执行结束: " + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 方法五:Future
         * @throws Exception
         */
        public static void main(String[] args) throws Exception {
            Future future = pool.submit(() -> sleepMethod(1));
            while (!future.isDone()){
                Thread.sleep(1000);
                System.out.println("还没停止。。。");
            }
            System.out.println("全部执行完毕");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    优缺点分析
    优点:使用简单,不需要关闭线程池。
    缺点:每个提交给线程池的任务都会关联一个Future对象,这可能会引入额外的内存开销。如果需要处理大量的任务,可能会占用较多的内存。

    测试代码汇总

    package pool;
    
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 五种判断线程池任务执行完成的方式
     * @author 百里
     */
    public class BaiLiIsShutdownThreadPoolDemo {
        /**
         * 创建一个最大线程数15的线程池
         */
        public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
                10,
                15,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
        /**
         * 线程执行方法,随机等待0到10秒
         */
        private static void sleepMethod(int index){
            try {
                long sleepTime = new Double(Math.random() * 10000).longValue();
                Thread.sleep(sleepTime);
                System.out.println("当前线程执行结束: " + index);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 方法一:isTerminated
         * @param args
         * @throws InterruptedException
         */
        public static void isTerminatedTest(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> sleepMethod(index));
            }
            pool.shutdown();
            while (!pool.isTerminated()){
                Thread.sleep(1000);
                System.out.println("还没停止。。。");
            }
            System.out.println("全部执行完毕");
        }
    
    
        /**
         * 方法二:getCompletedTaskCount
         * @param args
         * @throws InterruptedException
         */
        public static void completedTaskCountTest(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> sleepMethod(index));
            }
            //当线程池完成的线程数等于线程池中的总线程数
            while (!(pool.getTaskCount() == pool.getCompletedTaskCount())) {
                System.out.println("任务总数:" + pool.getTaskCount() + "; 已经完成任务数:" + pool.getCompletedTaskCount());
                Thread.sleep(1000);
                System.out.println("还没停止。。。");
            }
            System.out.println("全部执行完毕");
        }
    
        /**
         * 方法三:CountDownLatch
         * @throws Exception
         */
        public static void countDownLatchTest(String[] args) throws Exception {
            //计数器,判断线程是否执行结束
            CountDownLatch taskLatch = new CountDownLatch(10);
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> {
                    sleepMethod(index);
                    taskLatch.countDown();
                    System.out.println("当前计数器数量:" + taskLatch.getCount());
                });
            }
            //当前线程阻塞,等待计数器置为0
            taskLatch.await();
            System.out.println("全部执行完毕");
        }
    
        private static int taskNum = 0;
    
        /**
         * 方法四:公共计数
         * @throws Exception
         */
        public static void countTest(String[] args) throws Exception {
            Lock lock = new ReentrantLock();
            for (int i = 0; i < 10; i++) {
                int index = i;
                pool.execute(() -> {
                    sleepMethod(index);
                    lock.lock();
                    taskNum++;
                    lock.unlock();
                });
            }
            while(taskNum < 10) {
                Thread.sleep(1000);
                System.out.println("还没停止。。。当前完成任务数:" + taskNum);
            }
            System.out.println("全部执行完毕");
        }
    
        /**
         * 方法五:Future
         * @throws Exception
         */
        public static void futureTest(String[] args) throws Exception {
            Future future = pool.submit(() -> sleepMethod(1));
            while (!future.isDone()){
                Thread.sleep(1000);
                System.out.println("还没停止。。。");
            }
            System.out.println("全部执行完毕");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
  • 相关阅读:
    每天一个前端小知识02——Vue响应原理
    单例设计模式
    【MySQL】表的基础增删改查
    Java设计模式总结
    ubuntu22.04 配置共享文件夹 找不到/mnt/hgfs
    分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测
    5分钟搭建图片压缩应用
    卷积神经网络
    Linux:Command ‘vim‘ not found, but can be installed with:
    Excel中截取特殊字符之前、之间、之后的数据
  • 原文地址:https://blog.csdn.net/u011305680/article/details/134540229