自从昨天的一波bug吃了亏,我决定狠狠的总结一番线程池的拒绝策略
bug回顾:
我昨天在跑批,读取文件中的流水信息更新账单,量也还好,大概几万单吧,为了快点我就直接用我现成的一个线程池的工具类开多个线程跑,通过关注日志,发现怎么没过多久就跑完了。我一开始就以为就是处理的比较快,但是我昨天统计实收实付的时候发现怎么还这么多数据未实收实付,一开始看了很多单发现还好,但逐渐往后看怎么modified time都不太对呀,感觉像没有更新到。
此时,我重新修改了日志信息,新增了统计,就拿刚刚的重新跑了一遍,发现怎么只跑了1W多就停了。服务也没有跪,异常信息也没有,一切很自然的中断了。我第一反应是不是自己异常处理的有问题,把异常吃掉了,检查完发现也没啥问题。
那就纳闷了,就算量大把内存撑爆了也该报错呀,这啥都好好的。 因为本身这部分逻辑本身也不太复杂,逐行回看代码的时候,一个线程池的工具类吸引了我的注意。我点进去一看配置,核心线程配的5, 最大线程 50 , 阻塞队列 10000 ,拒绝策略是DiscardPolicy(丢弃任务且不抛弃异常) 看完我简直要哭了呀!
怪不得每次跑到1W多点就没掉了,任务插入的速度太快了,一开始核心线程沾满了,就开始插入10000条到队列,这时候还在疯狂插入任务,且没有线程空闲,只能继续增加线程到最大线程数50,依旧没有线程空闲,且任务仍在不断插入,所以后续的任务全部被直接丢弃了。
第一条,就是我要重申一遍,四种拒绝策略,巩固一下:
- AbortPolicy 丢弃任务,抛出异常
- DiscardPolicy 丢弃任务,且不抛出异常
- DiscardOldestPolicy 丢弃队列最前面的,接入最新的(喜新厌旧)
- CallerRunsPolicy 阻塞,由调用线程处理该任务
然后我想说,
不要贪方便,随便用别人的线程池,充分考虑自己的场景进行线程池的相关配置。
其实回过头来看,我最初用的那个线程池是不是有点限流的意思!
那么问题来了,这个问题怎么改会比较好?
方案一,改成无界队列
方案二:拒绝策略改成CallerRunsPolicy
方案三: 走队列,慢慢消费(比如mq,让多实例消费)
我是用的第二种,第一种我怕把内存撑爆了,第二种阻塞我觉得我能接受,问题不大。 期待评论区的最佳方案,哈哈哈哈