线程池拒绝策略——CallerRunsPolicy

    技术2022-07-10  103

    最近在搞线程池,用到了CallerRunsPolicy这个拒绝策略。

    为什么选用CallerRunsPolicy?

    因为考虑到数据一条都不能丢失,所以选择了CallerRunsPolicy策略。保证数据来了不会丢。天真的幻想着这策略好啊! 经过了一段时间的实战考验,发现了一个问题,数据处理不过来,经常堆积,这样还行能接受。

    遇到什么问题?

    在从单线程改用多线程后,阻塞队列在高峰期时经常是满的。 阻塞队列经常是满了,于是考虑从处理效率上入手,(同事解决的)优化了程序处理速度和数据库写入效率,缓解了这个问题,线程池还是用的最原始的版本,网上一搜一大把那种。这个版本上线运行了,简称 V-1.0。

    上面的问题解决了之后,新的问题就出现了。 因为线上运行了一段时间,运行几天之后就需要重启一下,那么就需要再进行第二波的优化。 第二波优化了线程池,数据接收后再用队列做了分流,堪称更稳定的版本,数据几乎没有延迟。

    于是经过改造,使用了新的方法去应用,运行了几天并观察对比和线上的数据。 于是问题就来了。 有些数据比较多的,线上和测试环境的数据,有些大部分对得上,有些大部分对不上,这就很奇怪了。

    经过对比排查: 因为线上运行的线程池是旧版本V-1.0,拒绝策略选择了CallerRunsPolicy。因为有些处理逻辑是需要数据时间顺序进行处理的,在高峰期的时候,大量的数据阻塞在阻塞队列里面等待处理:

    当线程数线程池的最大线程数并且阻塞队列已满的情况下,后到的数据会执行拒绝策略,让调用线程(提交任务的线程)直接执行此任务,导致数据处理顺序不一致。

    举个例: 线程数满并且阻塞队列满,阻塞队列中存在线程等待, 数据的顺序为 【11,12,13,14,15,16,17,18】 前面线程正在处理,后面有新数据【19】,【20】到来,发现队列满了,线程数也到最大了, 那就让调用线程执行这个任务,所以就会出现以下的处理顺序: (11,12,19,13,14……)等等类似的情况。

    再写个代码来说明一下吧

    ThreadPoolExecutor executorA = new ThreadPoolExecutor(4,4, 10L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); for (int i = 0; i < 500; i++) { int s = i; executorA.execute(()->{ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("【" + LocalTime.now() + "】线程 " + Thread.currentThread() + "正在执行任务" + s); }); }

    部分运行结果

    【09:32:15.570】线程 Thread[pool-1-thread-1,5,main]正在执行任务0 【09:32:15.570】线程 Thread[pool-1-thread-3,5,main]正在执行任务2 【09:32:15.570】线程 Thread[pool-1-thread-4,5,main]正在执行任务3 【09:32:15.570】线程 Thread[main,5,main]正在执行任务104 【09:32:15.570】线程 Thread[pool-1-thread-2,5,main]正在执行任务1 【09:32:15.771】线程 Thread[pool-1-thread-1,5,main]正在执行任务4 【09:32:15.772】线程 Thread[pool-1-thread-2,5,main]正在执行任务7 【09:32:15.772】线程 Thread[main,5,main]正在执行任务108 【09:32:15.772】线程 Thread[pool-1-thread-4,5,main]正在执行任务6 【09:32:15.773】线程 Thread[pool-1-thread-3,5,main]正在执行任务5 【09:32:15.972】线程 Thread[pool-1-thread-1,5,main]正在执行任务8 【09:32:15.973】线程 Thread[main,5,main]正在执行任务112 【09:32:15.973】线程 Thread[pool-1-thread-2,5,main]正在执行任务9 【09:32:15.973】线程 Thread[pool-1-thread-4,5,main]正在执行任务10 【09:32:15.974】线程 Thread[pool-1-thread-3,5,main]正在执行任务11 【09:32:16.173】线程 Thread[pool-1-thread-1,5,main]正在执行任务12 【09:32:16.174】线程 Thread[pool-1-thread-2,5,main]正在执行任务13 【09:32:16.174】线程 Thread[pool-1-thread-4,5,main]正在执行任务14 【09:32:16.174】线程 Thread[main,5,main]正在执行任务117 【09:32:16.175】线程 Thread[pool-1-thread-3,5,main]正在执行任务15 【09:32:16.374】线程 Thread[pool-1-thread-1,5,main]正在执行任务16 【09:32:16.375】线程 Thread[pool-1-thread-2,5,main]正在执行任务17 【09:32:16.375】线程 Thread[main,5,main]正在执行任务123 【09:32:16.375】线程 Thread[pool-1-thread-4,5,main]正在执行任务18 【09:32:16.376】线程 Thread[pool-1-thread-3,5,main]正在执行任务19 【09:32:16.575】线程 Thread[pool-1-thread-1,5,main]正在执行任务20 【09:32:16.576】线程 Thread[pool-1-thread-4,5,main]正在执行任务22 【09:32:16.576】线程 Thread[main,5,main]正在执行任务127 【09:32:16.576】线程 Thread[pool-1-thread-2,5,main]正在执行任务21 【09:32:16.577】线程 Thread[pool-1-thread-3,5,main]正在执行任务23 【09:32:16.776】线程 Thread[pool-1-thread-1,5,main]正在执行任务24 【09:32:16.777】线程 Thread[main,5,main]正在执行任务132 【09:32:16.777】线程 Thread[pool-1-thread-4,5,main]正在执行任务25 【09:32:16.777】线程 Thread[pool-1-thread-2,5,main]正在执行任务26 【09:32:16.778】线程 Thread[pool-1-thread-3,5,main]正在执行任务27 【09:32:16.977】线程 Thread[pool-1-thread-1,5,main]正在执行任务28 【09:32:16.978】线程 Thread[pool-1-thread-2,5,main]正在执行任务30 【09:32:16.978】线程 Thread[main,5,main]正在执行任务137

    结果很明显了。

    当在多线程中数据处理时需要强关联数据时间顺序时,最好考虑一下其他的处理方式,避免踩坑。

    Processed: 0.011, SQL: 12