电商项目之如何判断线程池是否执行完所有任务

news2024/12/27 10:08:36

在这里插入图片描述

文章目录

  • 1 问题背景
  • 2 前言
  • 3 4种常用的方法
  • 4 代码
    • 4.1 isTerminated()
    • 4.2 线程池的任务总数是否等于已执行的任务数
    • 4.3 `CountDownLatch`计数器
    • 4.4 `CyclicBarrier`计数器

1 问题背景

真实生产环境的电商项目,常使用线程池应用于执行大批量操作达到高性能的效果。应用场景有批量补偿修正数据库历史数据、定时批量执行业务逻辑(涉及到百万级数据)、批量初始化新业务的数据等等。用到线程池,必须要知道任务是否执行完了,才能进行下一步业务操作。今天总结归纳4种常用的方法判断线程池是否执行完所有任务

2 前言

先给出解决方案,文末再贴出详细代码
参考自:面试突击35:如何判断线程池已经执行完所有任务了?

3 4种常用的方法

  1. 线程池提供的isTerminated()方法。缺点是需要调用shutdown()关闭线程池
  2. 判断线程池的任务总数是否等于已执行的任务数。优点是无需关闭线程池。缺点是两个数值都是动态计算的,只是一个近似值
  3. CountDownLatch计数器。写法很优雅,且无需关闭线程池,但它的缺点是只能使用一次,CountDownLatch 创建之后不能被重复使用
  4. CyclicBarrier计数器。和 CountDownLatch 类似,它可以理解为一个可以重复使用的循环计数器,CyclicBarrier 可以调用reset()将自己重置到初始状态

4 代码

4.1 isTerminated()

@Slf4j
public class IsTerminatedDemo {
    private static final int BLOCKING_QUEUE_CAPACITY = 100;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");


    /**
     * 使用isTerminated判断线程池是否执行完任务,缺点是要关闭线程池
     *
     * @param args
     */
    public static void main(String[] args) {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                10,
                10,
                10 * 60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(BLOCKING_QUEUE_CAPACITY),
                new DefaultThreadFactory("complete_thread_pool"),
                new ThreadPoolExecutor.AbortPolicy());

        // 添加任务
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                // 随机休眠
                int r = new Random().nextInt(5);
                try {
                    Thread.sleep(r);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.info("Task NO.{} finish.", finalI);
            });
        }

        threadPool.shutdown();
        // 判断线程池是否执行完所有任务,前提是要执行shutdown
        while (!threadPool.isTerminated()) {
            log.info("{}: ThreadPool handleing task.", LocalDateTime.now().format(FORMATTER));
        }

        log.info("All tasks have been finished!");
    }
}

4.2 线程池的任务总数是否等于已执行的任务数

@Slf4j
public class GetCompletedTaskCountDemo {
    private static final int BLOCKING_QUEUE_CAPACITY = 100;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");


    /**
     * 判断线程池是否执行完所有任务,如果计划执行任务数=已完成任务数,那么线程池的任务就全部执行完了。
     * 优点是无需关闭线程池
     * 缺点是 getTaskCount() 和 getCompletedTaskCount() 返回的是一个近似值,因为线程池中的任务和线程的状态可能在计算过程中动态变化,所以它们两个返回的都是一个近似值
     *
     * @param args
     */
    public static void main(String[] args) {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                10,
                10,
                10 * 60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(BLOCKING_QUEUE_CAPACITY),
                new DefaultThreadFactory("complete_thread_pool"),
                new ThreadPoolExecutor.AbortPolicy());

        // 添加任务
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                // 随机休眠
                int r = new Random().nextInt(5);
                try {
                    Thread.sleep(r);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.info("Task NO.{} finish.", finalI);
            });
        }

        // 判断线程池是否执行完所有任务,如果计划执行任务数=已完成任务数,那么线程池的任务就全部执行完了。
        // 优点是无需关闭线程池
        // 缺点是 getTaskCount() 和 getCompletedTaskCount() 返回的是一个近似值,因为线程池中的任务和线程的状态可能在计算过程中动态变化,所以它们两个返回的都是一个近似值
        while (threadPool.getTaskCount() != threadPool.getCompletedTaskCount()) {
            log.info("{}: ThreadPool handleing task.", LocalDateTime.now().format(FORMATTER));
        }

        log.info("All tasks have been finished!");
    }
}

4.3 CountDownLatch计数器

@Slf4j
public class CountDownLatchDemo {
    private static final int BLOCKING_QUEUE_CAPACITY = 100;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");


    /**
     * 写法很优雅,且无需关闭线程池,但它的缺点是只能使用一次,CountDownLatch 创建之后不能被重复使用,
     * 也就是说 CountDownLatch 可以理解为只能使用一次的计数器
     *
     * @param args
     */
    public static void main(String[] args) {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                10,
                10,
                10 * 60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(BLOCKING_QUEUE_CAPACITY),
                new DefaultThreadFactory("complete_thread_pool"),
                new ThreadPoolExecutor.AbortPolicy());

        int taskCount = 5;
        CountDownLatch cdl = new CountDownLatch(taskCount);
        // 添加任务
        for (int i = 0; i < taskCount; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                // 随机休眠
                int r = new Random().nextInt(5);
                try {
                    Thread.sleep(r);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.info("Task NO.{} finish.", finalI);
                // 线程执行完,计数器减1
                cdl.countDown();
            });
        }

        log.info("{}: ThreadPool handleing task.", LocalDateTime.now().format(FORMATTER));
        try {
            // 阻塞等待所有线程执行完任务
            cdl.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        log.info("All tasks have been finished!");
    }
}

4.4 CyclicBarrier计数器

@Slf4j
public class CyclicBarrierDemo {
    private static final int BLOCKING_QUEUE_CAPACITY = 100;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");


    /**
     *和 CountDownLatch 类似,它可以理解为一个可以重复使用的循环计数器,CyclicBarrier 可以调用 reset 方法将自己重置到初始状态
     *
     * @param args
     */
    public static void main(String[] args) {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                10,
                10,
                10 * 60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(BLOCKING_QUEUE_CAPACITY),
                new DefaultThreadFactory("complete_thread_pool"),
                new ThreadPoolExecutor.AbortPolicy());

        int taskCount = 5;
        CyclicBarrier cb = new CyclicBarrier(taskCount, () -> log.info("log from CyclicBarrier, all tasks of ThreadPool have been finished"));
        // 添加任务
        for (int i = 0; i < taskCount; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                // 随机休眠
                int r = new Random().nextInt(5);
                try {
                    Thread.sleep(r);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                log.info("Task NO.{} finish.", finalI);
                // 阻塞等待
                try {
                    cb.await();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }

        log.info("All tasks have been finished!");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1953817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当全球银行系统“崩溃”时会发生什么?

有句名言&#xff1a;“当美国打喷嚏时&#xff0c;世界就会感冒……”换句话说&#xff0c;当人们对美国及其经济稳定性的信心下降时&#xff0c;其他经济体&#xff08;以及黄金、白银和股票等资产&#xff09;的价值往往会下降。 与任何其他资产类别一样&#xff0c;加密货…

Java并发编程深入浅出(个人造诣)

目录 串行&#xff0c;并行与并发&#xff1a; 并发编程的目的 如何保证多线程的安全&#xff08;并发编程思想&#xff09; 写后读思想&#xff1a;&#xff08;一个线程写后再让另一个线程读&#xff09; 如何保障写后读&#xff1f; 1、加锁&#xff1a;&#xff08;s…

Cocos Creator2D游戏开发(3)-飞机大战(1)-背景动起来

资源见: https://pan.baidu.com/s/1cryYNdBOry5A4YEEcLwhDQ?pwdzual 步骤 1, 让背景动起来 2, 玩家飞机显现,能操控,能发射子弹 3.敌机出现 4. 碰撞效果(子弹和敌机,敌机和玩家) 5. 积分和游戏结束 6. 游戏存档,对接微信小游戏,保存历史最高分 7. cocos发布到微信小游戏 资源…

【计算机网络】IP地址和子网掩码(IP地址篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️计算机网络】 文章目录 前言IP地址网络地址网络地址的定义与组成作用分类网络地址的分配与管理 广播地址&#xff08;Broadcast Address&#xff09;定义构成类型作用注意事项 广播地址功能 组播地址…

【初阶数据结构篇】栈的实现(赋源码)

文章目录 栈1 代码位置2 概念与结构1.1概念1.2结构 2 栈的实现2.1 栈的初始化和销毁2.1.1 初始化2.1.2 销毁 2.2 栈顶插入和删除数据2.2.1 栈顶插入数据&#xff08;压栈&#xff09;2.2.2 栈顶删除数据&#xff08;出栈&#xff09; 2.3 返回栈顶数据2.4 返回栈的有效数据个数…

Jenkins持续部署

开发环境任务的代码只要有更新&#xff0c;Jenkins会自动获取新的代码并运行 1. pycharm和git本地集成 获取到下面的 Git可执行文件路径 2. pycharm和gitee远程仓库集成 先在pycharm中安装gitee插件 在设置中找到gitee&#xff0c;点击添加账户&#xff0c;并将自己的账户添…

(day26)leecode热题——找到字符串中所有字母异位词

描述 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1: 输入: s "cbaebabacd", p …

【QGroundControl二次开发】六. QGC地面站中视频流配置gstreamer

本文解决qgc源码编译后无法接收视频推流&#xff0c;原因是缺少gstreamer。 下面为windows安装流程&#xff0c;qgc版本为4.4.1稳定版 1. 安装gstreamer 官网链接&#xff1a;https://gstreamer.freedesktop.org/download/#windows 两个都要下载。安装的时候&#xff0c;在cu…

网络安全自学从入门到精通的制胜攻略!!!

在信息时代&#xff0c;网络安全已成为至关重要的领域。越来越多的人希望通过自学掌握这门技术&#xff0c;开启充满挑战与机遇的职业道路。以下是一份精心为您打造的网络安全自学攻略&#xff0c;助您在自学之旅中乘风破浪。 一、明确目标与兴趣方向 网络安全涵盖众多领域&am…

Java的四种引用类型

Java的四种引用类型 1. 强引用&#xff08;Strong Reference&#xff09;2. 软引用&#xff08;Soft Reference&#xff09;3. 弱引用&#xff08;Weak Reference&#xff09;4. 虚引用&#xff08;Phantom Reference&#xff09; &#x1f496;The Begin&#x1f496;点点关注…

硬件工程师笔面试真题汇总

目录 1、电阻 1&#xff09;上拉电阻的作用 2&#xff09;PTC热敏电阻作为电源电路保险丝的工作原理 2、电容 1&#xff09;电容的特性 2) 电容的特性曲线 3) 1uf的电容通常来滤除什么频率的信号 3、电感 4、二极管 1&#xff09;二极管特性 2&#xff09;二极管伏安…

MySQL练手 --- 1633. 各赛事的用户注册率

题目链接&#xff1a;1633. 各赛事的用户注册率 思路&#xff1a; 两张表&#xff0c;一张为Users用户表&#xff0c;该表存储着所有用户的 user_id 和 user_name 另一张表为Register注册表&#xff0c;该表存储着赛事id&#xff08;contest_id&#xff09;&#xff0c;和已…

NumpyPandas:Pandas库(25%-50%)

目录 前言 一、列操作 1.修改变量列 2.筛选变量列 3.删除变量列 4.添加变量列 二、数据类型的转换 1.查看数据类型 2.将 ok的int类型转换成float类型 3.将ar的float类型转换成int类型 三、建立索引 1.建立DataFrame时建立索引 2.在读入数据时建立索引 3.指定某列或…

PHP8.3.9安装记录,Phpmyadmin访问提示缺少mysqli

ubuntu 22.0.4 腾讯云主机 下载好依赖 sudo apt update sudo apt install -y build-essential libxml2-dev libssl-dev libcurl4-openssl-dev pkg-config libbz2-dev libreadline-dev libicu-dev libsqlite3-dev libwebp-dev 下载php8.3.9安装包 nullhttps://www.php.net/d…

【Linux】生产者消费者模型 + 线程池的介绍和代码实现

前言 上节我们学习了线程的同步与互斥&#xff0c;学习了互斥锁和条件变量的使用。本章我们将学习编程的一个重要模型&#xff0c;生产者消费者模型&#xff0c;并且运用之前学的线程同步和互斥的相关接口来实现阻塞队列和环形队列&#xff0c;最后再来实现一个简易的线程池。 …

springboot电子产品销售系统-计算机毕业设计源码80294

摘 要 电子商务行业在全球范围内迅速发展&#xff0c;随之而来的是电子产品销售市场的快速增长和消费者对在线购物体验的需求提升&#xff0c;因此&#xff0c;电子产品销售系统应运而生。该系统旨在满足电子产品市场的需求&#xff0c;提供全面的购物功能和高效的管理操作。 …

严格模式 模块化开发

严格模式 当你在脚本或函数的顶部添加"use strict"语句时&#xff0c;你的代码将在严格模式下执行。这可以帮助你避免某些常见的编程陷阱&#xff0c;例如在不声明变量的情况下就使用它们&#xff0c;或者删除变量、函数或函数参数。在严格模式下&#xff0c;这样的…

进阶篇,内附代码:锂电池二阶模型-离线与在线参数辨识

锂电池二阶模型-在线参数辨识 背景二阶等效电路模型介绍二阶模型的离线参数辨识二阶模型的RLS表达式递推代码已知问题背景 锂电池一阶戴维南等效模型的参数辨识方法,已经在前面两期详细地讲解了一轮。 一阶模型-离线参数辨识一阶模型-在线参数辨识本期继续讲解一下如何进行二…

stm32——lcd液晶显示

一.液晶屏介绍 液晶显示屏是由液晶显示面板&#xff0c;电容触摸屏&#xff0c;pcb底板构成。在液晶显示屏里我们有带控制芯片的还有不带控制芯片的。对于低端的微控制器它不能直接控制液晶面板&#xff0c;所以需要给液晶控制面板而外增加一个液晶控制芯片。对于单片机stm32f4…

Photos框架 - 自定义媒体选择器(UI预览)

引言 在前面的博客中我们已经介绍了使用媒体资源数据的获取&#xff0c;以及自定义的媒体资源选择列表页。在一个功能完整的媒体选择器中&#xff0c;预览自然是必不可少的&#xff0c;本篇博客我们就来实现一个资源的预览功能&#xff0c;并且实现列表和预览的数据联动效果。…