一个适合绝大多数场景下的批量任务线程池

来源:互联网 发布:手机淘宝兼职客服招聘 编辑:程序博客网 时间: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();        }    }}
阅读全文
0 0
原创粉丝点击