大基数开票任务的优化

发布时间:2026/10/5 6:51:51
大基数开票任务的优化 业务场景开发票任务遍历几十万广告账户属于长耗时批量任务。原有共用全局线程池开票任务执行时大量线程被长期占用挤压定时任务、消息消费等其他业务任务引发线程耗尽、任务堆积、线上报警。解决思路ConcurrentHashMap作为资源容器装各个部分的线程池其中key为任务标识、value为对应独立线程池实现开票任务和其他任务所使用的线程池独立。区分全局线程池留给短耗时任务业务隔离线程池开票、大批量数据遍历这类长耗时任务独立资源代码实现依赖Java SpringBoot 环境即可Ⅰ.线程池管理器Java类public class BusinessThreadPoolManager { //新建ConcurrentHashMap来装 private final MapString, ThreadPoolExecutor businessThreadPoolMap new ConcurrentHashMap(); //原生线程池核心实现类根据业务写参数 //这里代码只传了 3 个核心参数String businessKey是额外的标识其他四个参数全用默认值 public ThreadPoolExecutor getOrCreateBusinessPool( String businessKey, int corePoolSize, int maximumPoolSize, int queueCapacity ){ return ...//线程池内部看下面代码 } }1.提交任务到对应线程池2.销毁指定业务线程池释放资源3.销毁全部业务线程池服务关闭时调用线程池内部行为代码return bizThreadPoolMap.computeIfAbsent( businessKey, k - { // 自定义线程工厂方便日志定位开票任务线程 ThreadFactory threadFactory new ThreadFactory() { private final AtomicInteger num new AtomicInteger(1); //即初始值为1 Override public Thread newThread(Runnable r) { Thread t new Thread(r, business- businessKey -thread- num.getAndIncrement()); t.setDaemon(false); return t; } }; //直接返回最终确定好的线程池即可 return new ThreadPoolExecutor( corePoolSize, maximumPoolSize, IDLE_TIMEOUT_SECONDS, //已提前定义好为300文章中未提及 TimeUnit.SECONDS, new LinkedBlockingQueue(queueCapacity), threadFactory, new ThreadPoolExecutor.CallerRunsPolicy() ); });其中1.computeIfAbsent如果 Map 中 key 不存在或者value 为 null 则执行 mappingFunction 生成 value放入 map返回新 value如果 key 已经存在直接返回已存在的 value不会执行 mappingFunction2.AtomicInteger:原子整型用来在多线程环境下安全地做整数读写、自增不需要 synchronized 加锁。 底层基于 CASCompare-And-Swap比较并交换属于无锁并发工具3.Runnable接口:区分Runnable任务干什么事Thread执行任务的工人线程4.setDaemon(false) 守护线程daemontrue后台线程。当所有非守护线程全部结束JVM 直接退出守护线程会被直接终止不等它跑完。典型例子GC 线程。用户线程 / 非守护线程daemonfalse默认值前台业务线程。JVM 必须等所有用户线程执行完毕JVM 才会正常退出。5.CallerRunsPolicy()天然限流提交任务的线程被卡住提交速度被迫降下来防止疯狂往线程池压任务Ⅱ.开票任务服务类InvoiceTaskService.java几十万广告账户遍历开票任务提交到独立开票线程池不再使用全局池。public void batchGenerateInvoice(ListLong adAccountIdList) { // 动态获取开票专属线程池参数 // 可配置在yml文件根据账户数量动态调整这里示例固定值 int core 4; int max 8; int queueCap 200; ThreadPoolExecutor invoicePool bizThreadPoolManager.getOrCreateBizPool(INVOICE_BIZ_KEY, core, max, queueCap); // 遍历几十万广告账户拆分任务提交到开票独立线程池 for (Long accountId : adAccountIdList) { Runnable invoiceSubTask () - { try { generateSingleAccountInvoice(accountId); } catch (Exception e) { // 异常埋点、告警上报 e.printStackTrace(); } }; invoicePool.submit(invoiceSubTask); } }总结这里的getOrCreateBusinessPool看似还是普通线程池工具但其实

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询