一个适合绝大多数场景下的批量任务线程池
来源:互联网 发布:手机淘宝兼职客服招聘 编辑:程序博客网 时间:2024/05/22 12:06
前言
在工作中的一个场景中,需要紧急处理五千多份合同,合同处理的过程有些复杂,这里说的复杂不是代码复杂,是中间需要经过多个服务渠道,包括对外对接的一些业务,所以这五千来分如果同步处理的话,估计要跑上估计至少也得半天的时间了,而后期确定了还会面临同样的问题(坑爹的代码与凌乱的架构问题),因此写了一个处理合同的线程池,越写兴趣越浓,最后写出来以后发现这个鸟玩意儿实在是太通用了,几乎可以用在所有场景下的批量任务。
简述
这个线程池可以说是为批量任务量身定做的一套方案,并且几乎可以实现任何场景下的批量任务。大体分为4个部分:
- 约束者
- 执行者
- 管理者
- 发起者
约束者
约束者主要是对整个线程执行类的一个约束,他定义了公共的一致的接口,方便其他角色调度,我给他起名为ThreadTask的一个借口类,以下是代码,其中T表示要处理的数据类型:
package com.cnpany.common.util.thread;import com.cnpany.common.util.thread.exception.ThreadTaskIncompleteExecutionException;/** * 多线程任务 * @author 郭胜凯 * @time 2017年10月28日 上午11:28:23 * @email guoshengkai@shoujinwang.com */public interface ThreadTask<T> { /** * 开始线程 */ void start(); /** * 结束线程 */ void stop(); /** * 获得任务数 * @return */ int getTaskCount(); /** * 获得线程数 * @return */ int getThreadCount(); /** * 等待完成 * @return * 等待时间 */ long doWait(); /** * 等待完成 * @param wautTime * 最长等待时间 * @return * 实际等待时间 */ long doWait(long wautTime) throws ThreadTaskIncompleteExecutionException;}
执行者
执行者类似一个员工的角色,他负责执行整个批量任务的实现,我更喜欢叫他'机器人'
所以,他也是一个接口类,这个类只有一个excute方法,下面是这个机器人
的具约束码,其中T表示要处理的数据类型,K表示在处理过程中需要用到的对象。因为我需要对数据库以及其他的一些远程业务进行操作,所以我需要把Service交给这个机器人,让机器人用这个Service去做一些事情。
package com.cnpany.common.util.thread;/** * 线程工作者 * @author 郭胜凯 * @time 2017年10月28日 上午11:25:05 * @email guoshengkai@shoujinwang.com */public interface ThreadRobot<T, K> { /** * 工作函数 * @param t */ void excute(T t, K param);}
管理者
这里的管理者,是针对Thread Task这个接口的实现,我给他起名为BatchThreadTask,也就是说,这个管理者承担一个批处理任务的管理工作,当然,根据不同的业务需求,也可以创建更多的管理者,不签这个批处理的角色几乎可以实现绝大多数业务的批量操作了,下面是具体代码:
package com.cnpany.common.util.thread.impl;import java.util.LinkedList;import java.util.List;import java.util.NoSuchElementException;import java.util.concurrent.locks.Lock;import com.cnpany.common.util.thread.ThreadRobot;import com.cnpany.common.util.thread.ThreadTask;import com.cnpany.common.util.thread.exception.NotStartedException;import com.cnpany.common.util.thread.exception.NullTaskException;import com.cnpany.common.util.thread.exception.ThreadTaskIncompleteExecutionException;/** * 批量线程任务 * @author 郭胜凯 * @time 2017年10月28日 上午11:33:05 * @email guoshengkai@shoujinwang.com */public class BatchThread<T, K> implements ThreadTask<T> { /** * 消费内容 */ private LinkedList<T> param = new LinkedList<>(); /** * 最大线程数 */ private int maxThread = 3; /** * 当前线程数 */ private int thisThread = 0; /** * 执行机器人 */ private ThreadRobot<T, K> roboot; private long startTime = 0; private K member = null; private Lock lock = null; private boolean over = false; private boolean exit = false; /** * 创建批量任务 * @param roboot * 任务执行机器人 * @param maxThread * 最大机器人数量 * @param param * 消费内容 */ public BatchThread(ThreadRobot<T, K> roboot, int maxThread, List<T> param, Lock lock, K member) { if(null == param || param.isEmpty()) { throw new NullTaskException(); } if(maxThread > 0) { this.maxThread = maxThread; } for (T t : param) { this.param.addLast(t); } this.roboot = roboot; this.lock = lock; this.member = member; } @Override public void start() { startTime = System.currentTimeMillis(); for(int i = 1; i <= maxThread; i++) { Thread t = new Thread(new Runnable() { @Override public void run() { try { T item = null; while((item = getList()) != null) { if(exit) { break; } roboot.excute(item, member); } }finally { synchronized (lock) { thisThread --; if(thisThread == 0) { over = true; } } } } }); t.start(); thisThread ++; } } @Override public void stop() { //不实现 } @Override public int getTaskCount() { return param.size(); } @Override public int getThreadCount() { // TODO Auto-generated method stub return thisThread; } @Override public long doWait() { while(!over) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } continue; } if(startTime == 0) { throw new NotStartedException(); } return System.currentTimeMillis() - startTime; } private T getList() { synchronized (lock) { try { return param.removeFirst(); } catch (NoSuchElementException e) { return null; } } } @Override public long doWait(long wautTime) throws ThreadTaskIncompleteExecutionException { while(!over) { if(System.currentTimeMillis() - startTime > wautTime) { //强制结束 exit = true; if(!over) { throw new ThreadTaskIncompleteExecutionException(); } } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } continue; } if(startTime == 0) { throw new NotStartedException(); } return System.currentTimeMillis() - startTime; }}
发起者
发起者是一个工厂类,他的作用就是通过创建管理者的角色让它去执行你交给他的任务。其实很简单,不多说了,上代码:
package com.cnpany.common.util.thread.factory;import java.util.List;import java.util.concurrent.locks.ReentrantLock;import com.cnpany.common.util.thread.ThreadRobot;import com.cnpany.common.util.thread.ThreadTask;import com.cnpany.common.util.thread.impl.BatchThread;/** * 线程任务工厂 * @author 郭胜凯 * @time 2017年10月28日 上午11:27:25 * @email guoshengkai@shoujinwang.com */public class ThreadTaskFactory { /** * 创建批量任务线程池 * @param roboot * 工作机器人 * @param maxThread * 线程数 * @param quqe * 消费队列 * @param param * 机器人用到的参数 * @return */ public static<T, K> ThreadTask<T> createBatch(ThreadRobot<T, K> roboot, int maxThread, List<T> quqe, K param) { return new BatchThread<T, K>(roboot, maxThread, quqe, new ReentrantLock(), param); }}
OK!到此为止,这个线程池就算是完工了,具体怎么去用呢,我们只需要通过ThreadTaskFactory来创建这么一个批处理任务,也就是传机器人(工作者)需要做的实现逻辑就好,这里我是直接这么做的:
/** * 创建智享方案协议并签章 */@Scheduled(cron = "0/20 * * * * ? ")public void doCreateIntelAfreement() { logger.info("[智享方案协议]-----------------------------扫描待签章的序列"); List<String> accounts = commonSignService.listIntelAfreementAccounts(); if(!accounts.isEmpty()) { //定义批量处理任务线程池 ThreadTask<String> batchSign = ThreadTaskFactory.createBatch(new ThreadRobot<String, CommonSignService>() { @Override public void excute(String accountId, CommonSignService service) { CommonAttach attach = service.createIntelAfreementPdf(accountId); //生成文件 String afreementId = service.InsertIntelTenderAfreement(accountId, attach.getId()); //协议入库 service.signIntelAfreementPdf(afreementId); //协议签章 } }, 20, accounts, commonSignService); logger.info("[智享方案协议]-----------------------------开始批处理"); batchSign.start(); try { batchSign.doWait(18000); logger.info("[智享方案协议]-----------------------------批处理完毕"); } catch (ThreadTaskIncompleteExecutionException e) { logger.info("[智享方案协议]-----------------------------超时未完成,批处理强制退出"); e.printStackTrace(); } }}
- 一个适合绝大多数场景下的批量任务线程池
- Java 线程 callable 适合android的任务
- 一个批量结算任务的demo
- 适合自动化测试的场景
- 一个线程池与任务池相结合的案例
- 一个用来“拉”任务的简单线程池 c#版
- 基于SmartThreadPool线程池技术实现多任务批量处理
- Java使用线程池批量顺序执行任务
- Linux下使用两个线程协作完成一个任务的简易实现
- 打造一个可分割的华丽圆形进度条,适合倒计时等场景
- 什么情形下适合使用线程池技术
- cardova(Phone GAP)适合应用的场景
- lvs,nginx,haproxy的优缺点,适合场景
- RxJava中BehaviorSubject适合的使用场景
- hadoop适合与不适合的应用场景
- 多线程线程池控制一个方法的并发量 限制只有5个线程执行任务
- java线程wait()方法的一个应用场景
- 线程任务池的书写
- Can not connect to the Service chromedriver的解决方法
- 深入Java集合系列:ArrayList的实现原理
- 基于WebMagic写的一个csdn博客小爬虫
- codeforces 877 problem E Danil and a Part-time Job 【dfs序 + 线段树区间异或修改】
- lambda expressions are not supported at this language level
- 一个适合绝大多数场景下的批量任务线程池
- Android Studio导入github下载的project和module的正确姿势
- 选用pdf转cad最好的软件转换操作
- 64. Minimum Path Sum
- [vijos1070]新年趣事之游戏(次小生成树)
- 图的储存结构(*链式前向星)
- 《Java专栏》— 接口
- Xcode执行Analyze静态分析
- java的冒泡排序