线程池拒绝策略——CallerRunsPolicy

最近在搞线程池,用到了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

结果很明显了。

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

你可能感兴趣的:(多线程,java)