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

文章目录
- 1 问题背景
- 2 前言
- 3 4种常用的方法
- 4 代码
- 4.1 isTerminated()
- 4.2 线程池的任务总数是否等于已执行的任务数
- 4.3 `CountDownLatch`计数器
- 4.4 `CyclicBarrier`计数器
1 问题背景
真实生产环境的电商项目,常使用线程池应用于执行大批量操作达到高性能的效果。应用场景有批量补偿修正数据库历史数据、定时批量执行业务逻辑(涉及到百万级数据)、批量初始化新业务的数据等等。用到线程池,必须要知道任务是否执行完了,才能进行下一步业务操作。今天总结归纳4种常用的方法判断线程池是否执行完所有任务
2 前言
先给出解决方案,文末再贴出详细代码
参考自:面试突击35:如何判断线程池已经执行完所有任务了?
3 4种常用的方法
- 线程池提供的
isTerminated()方法。缺点是需要调用shutdown()关闭线程池 - 判断线程池的任务总数是否等于已执行的任务数。优点是无需关闭线程池。缺点是两个数值都是动态计算的,只是一个近似值
CountDownLatch计数器。写法很优雅,且无需关闭线程池,但它的缺点是只能使用一次,CountDownLatch 创建之后不能被重复使用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();// 判断线程池是否执行完所有任务,前提是要执行shutdownwhile (!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);// 线程执行完,计数器减1cdl.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!");}
}
相关文章:
电商项目之如何判断线程池是否执行完所有任务
文章目录 1 问题背景2 前言3 4种常用的方法4 代码4.1 isTerminated()4.2 线程池的任务总数是否等于已执行的任务数4.3 CountDownLatch计数器4.4 CyclicBarrier计数器 1 问题背景 真实生产环境的电商项目,常使用线程池应用于执行大批量操作达到高性能的效果。应用场景…...
【前端 15】Vue生命周期
Vue生命周期 在Vue.js中,了解组件的生命周期对于开发者来说是至关重要的。Vue的生命周期指的是Vue实例从创建到销毁的一系列过程,每个阶段都对应着特定的生命周期钩子(或称为生命周期方法),允许我们在不同的时间点加入…...
PCIe总线-Linux内核PCIe软件框架分析(十一)
1.简介 Linux内核PCIe软件框架如下图所示,按照PCIe的模式,可分为RC和EP软件框架。RC的软件框架分为五层,第一层为RC Controller Driver,和RC Controller硬件直接交互,不同的RC Controller,其驱动实现也不相…...
视觉SLAM第二讲
SLAM分为定位和建图两个问题。 定位问题 定位问题是通过传感器观测数据直接或间接求解位置和姿态。 通常可以分为两类:基于已知地图的定位和基于未知地图的定位。 基于已知地图的定位 利用预先构建的地图,结合传感器数据进行全局定位。SLAM中的全局…...
mysql1055报错解决方法
目录 一、mysql版本 二、 问题描述 三、解决方法 1.方法一(临时) 2.方法二(永久) 一、mysql版本 mysql版本:5.7.23 二、 问题描述 在查询时使用group by语句,出现错误代码:1055…...
Java的@DateTimeFormat注解与@JsonFormat注解的使用对比
Java的DateTimeFormat注解与JsonFormat注解的使用对比 在Java开发中,处理日期和时间格式时,我们经常会使用到DateTimeFormat和JsonFormat注解。这两个注解主要用于格式化日期和时间,但在使用场景和功能上有所不同。本文将详细介绍这两个注解…...
德国云手机:企业移动办公解决方案
在现代商业环境中,移动办公已经成为一种趋势。德国云手机作为一种高效的解决方案,为企业提供了强大的支持。本文将探讨德国云手机如何优化企业的移动办公环境。 一、德国云手机的主要优势 高灵活性 德国云手机具有高度的灵活性,能够根据用户需…...
【React】useState:状态管理的基石
文章目录 一、什么是 useState?二、useState 的基本用法三、useState 的工作原理四、高级用法五、最佳实践 在现代前端开发中,React 是一个非常流行的库,而 useState 是 React 中最重要的 Hook 之一。useState 使得函数组件能够拥有自己的状态…...
商品中心关于缓存热key的解决方案
缓存热key一旦被击穿,流量势必会打到数据库,如果数据库崩了,游戏直接结束。 从两点来讨论:如何监控、如何解决。 如何监控 通过业务评估:比如营销活动推出的商品或者热卖的商品。基于LRU的命令,redis-cl…...
【Python系列】Parquet 数据处理与合并:高效数据操作实践
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
大脑自组织神经网络通俗讲解
大脑自组织神经网络的核心概念 大脑自组织神经网络,是指大脑中的神经元通过自组织的方式形成复杂的网络结构,从而实现信息的处理和存储。这一过程涉及到神经元的生长、连接和重塑,是大脑学习和记忆的基础。其核心公式涉及神经网络的权重更新…...
org.springframework.context.annotation.DeferredImportSelector如何使用?
DeferredImportSelector 是 Spring 框架中一个比较高级的功能,主要用于在 Spring 应用上下文的配置阶段延迟导入某些组件或配置。这个功能特别有用,比如在处理依赖于其他自动配置的场景,或者当你想基于某些条件来决定是否导入特定的配置类时。…...
缓慢变化维
缓慢变化维 缓慢变化维(Slowly Changing Dimensions,简称SCD)是数据仓库中的一个重要概念,用于处理维度表中数据随时间发生的变化。以下是一个具体的例子来描述缓慢变化维: 假设我们有一个销售数据仓库,其…...
Vue常用的指令都有哪些?都有什么作用?什么是自定义指令?
常用指令: 1、v-model 多用于表单元素实现双向数据绑定 (同angular中的ng-model) 2、v-for格式: v-for"字段名in(of)数组json"循环数组或json(同angular中的ng repeat),需要注意从vue2开始取消了$index 3、v-show 4、v-hide 隐藏内容 (同a…...
kettle从入门到精通 第八十一课 ETL之kettle kettle中的json对象字段写入postgresql中的json字段正确姿势
1、上一节可讲解了如何将json数据写入pg数据库表中的json字段,虽然实现了效果,但若客户继续使用表输出步骤则仍然无法解决问题。 正确的的解决方式是设置数据库连接参数stringtypeunspecified 2、stringtypeunspecified 参数的作用: 当设置…...
计算机网络实验-RIP配置与分析
前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 一、相关知识 路由信息协议(Routing Information Protocol,RIP)是一种基于距离向量(Distance-Vector&…...
33.【C语言】实践扫雷游戏
预备知识: 第13篇 一维数组 第13.5篇 二维数组 第28篇 库函数 第29篇 自定义函数 第30篇 函数补充 0x1游戏的运行: 1.随机布置雷 2.排雷 基本规则: 点开一个格子后,显示1,对于9*9,代表以1为中心的去…...
git学习笔记(总结了常见命令与学习中遇到的问题和解决方法)
前言 最近学习完git,学习过程中也遇到了很多问题,这里给大家写一篇总结性的博客,主要大概讲述git命令和部分难点问题(简单的知识点这里就不再重复讲解了) 一.git概述 1.1什么是git Git是一个分布式的版本控制软件。…...
【计算机网络】TCP协议详解
欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 1、引言2、udp和tcp协议的异同3、tcp服务器3.1、接口认识3.2、服务器设计 4、tcp客户端4.1、客户端设计4.2、说明 5、再研Tcp服务端5.1、多进程版5.2、多线程版 5、守护进程化5.1、什么是守护进程5.2…...
2.3 大模型硬件基础:AI芯片(上篇) —— 《带你自学大语言模型》系列
本系列目录 《带你自学大语言模型》系列部分目录及计划,完整版目录见:带你自学大语言模型系列 —— 前言 第一部分 走进大语言模型(科普向) 第一章 走进大语言模型 1.1 从图灵机到GPT,人工智能经历了什么࿱…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
