自定义创建线程池处理任务,如果线程池容量满了,自动将任务放进缓冲队列的简单实现

@Slf4j
@Component
public class CustomThreadPoolManager {
    // 线程池维护线程的最少数量
    private final static int CORE_POOL_SIZE = 2;
    // 线程池维护线程的最大数量
    private final static int MAX_POOL_SIZE = 10;
    // 线程池维护线程所允许的空闲时间
    private final static int KEEP_ALIVE_TIME = 0;
    // 线程池所使用的缓冲队列大小
    private final static int WORK_QUEUE_SIZE = 50;

    /**
     * 用于储存在队列中的试卷,防止重复提交,在真实场景中,可用redis代替 验证重复
     */
    Map<String, Object> cacheMap = new ConcurrentHashMap<>();


    /**
     * 试卷的缓冲队列,当线程池满了,则将试卷存入到此缓冲队列
     */
    Queue<Object> msgQueue = new LinkedBlockingQueue<Object>();


    /**
     * 当线程池的容量满了,执行下面代码,将试卷存入到缓冲队列
     */
    final RejectedExecutionHandler handler = new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            //试卷加入到缓冲队列
            msgQueue.offer(((ExamPaperThread) r).getPaperKey());
            log.info("系统任务太忙了,把此试卷交给(调度线程池)逐一处理,试卷号:"
                    + ((ExamPaperThread) r).getPaperKey());
        }
    };


    /**创建线程池*/
    final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
            MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
            new ArrayBlockingQueue(WORK_QUEUE_SIZE), this.handler);


    /**将任务加入试卷线程池*/
    public void addExamPaper(String paperKey, List<ExamPaperResponsePo> po){
        log.info("此试卷准备添加到线程池,试卷号:" + paperKey);
        //通过试卷ID验证当前进入的试卷是否已经存在
        if (cacheMap.get(paperKey) == null) {
            log.info("此试卷已添加到线程池,试卷号:" + paperKey);
            cacheMap.put(paperKey, po);
            ExamPaperThread examPaperThread = new ExamPaperThread(paperKey, po);
            threadPool.execute(examPaperThread);
        }
    }

    /**
     * 线程池的定时任务----> 称为(调度线程池)。此线程池支持 定时以及周期性执行任务的需求。
     */
    final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);


    /**
     * 检查(调度线程池),每秒执行一次,查看试卷的缓冲队列是否有 试卷记录,则重新加入到线程池
     */
    final ScheduledFuture scheduledFuture = scheduler.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            //判断缓冲队列是否存在记录
            if(!msgQueue.isEmpty()){
                //当线程池的队列容量少于WORK_QUEUE_SIZE,则开始把缓冲队列的试卷 加入到 线程池
                if (threadPool.getQueue().size() < WORK_QUEUE_SIZE) {
                    String paperKey = (String) msgQueue.poll();
                    List<ExamPaperResponsePo> po = null;
                    if(null != cacheMap.get(paperKey)){
                        po = (List<ExamPaperResponsePo>)cacheMap.get(paperKey);
                    }
                    ExamPaperThread examPaperThread = new ExamPaperThread(paperKey,po);
                    threadPool.execute(examPaperThread);
                    log.info("(调度线程池)缓冲队列出现试卷业务,重新添加到线程池,试卷号:"+paperKey);
                }
            }
        }
    }, 0, 1, TimeUnit.SECONDS);


    /**获取消息缓冲队列*/
    public Queue<Object> getMsgQueue() {
        return msgQueue;
    }

    /**终止试卷线程池+调度线程池*/
    public void shutdown() {
        //true表示如果定时任务在执行,立即中止,false则等待任务结束后再停止
        log.info("终止试卷线程池+调度线程池:"+scheduledFuture.cancel(false));
        scheduler.shutdown();
        threadPool.shutdown();

    }

}
@Slf4j
@Component
@Scope("prototype")//spring多例 单例会有线程安全问题 //我自己其实觉得好像单利多例并不影响什么。。。
public class ExamPaperThread implements Runnable {
	
	private RedisService redisService;
	
    private ObjectMapper mapper;
    //试卷ID
    private String paperKey;
    //试卷明细
    private List<ExamPaperResponsePo> examPaperResponsePoList;

    public List<ExamPaperResponsePo> getExamPaperResponsePoList() {
        return examPaperResponsePoList;
    }

    public void setExamPaperResponsePoList(List<ExamPaperResponsePo> examPaperResponsePoList) {
        this.examPaperResponsePoList = examPaperResponsePoList;
    }

    public String getPaperKey() {
        return paperKey;
    }

    public void setPaperKey(String paperKey) {
        this.paperKey = paperKey;
    }

    public ExamPaperThread() {
    }

    public ExamPaperThread(String paperKey, List<ExamPaperResponsePo> examPaperResponsePoList) {
        this.paperKey = paperKey;
        this.examPaperResponsePoList = examPaperResponsePoList;
    }

    @Override
    public void run() {
        log.info("线程启动:"+paperKey);
        for(ExamPaperResponsePo examPaperResponsePo : examPaperResponsePoList){
            try {
                this.redisService = SpringContextUtil.getBean(RedisService.class);
                this.mapper = SpringContextUtil.getBean(ObjectMapper.class);
                String paperId = examPaperResponsePo.getPaperId();
                int paperIndex = examPaperResponsePo.getPaperIndex();
                String key = RdpConstant.EXAM_CACHE_PREFIX + paperId + "_" + paperIndex;
                redisService.del(key);
                redisService.set(key, mapper.writeValueAsString(examPaperResponsePo));
                log.info("SaveExamPaper successful! key : " + key);
            } catch (Exception e) {
                log.error("SaveExamPaper failed! ", e);
            }
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值