服务宕机 重启等等 如何保证线程池中任务不丢失

1,540 阅读2分钟

线程池有哪些问题

3.1 队列过大

Executors.newFixedThreadPool,它可以创建固定线程数量的线程池,任务队列使用的是LinkedBlockingQueue,默认最大容量是Integer.MAX_VALUE。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, 
                               nThreads,
                                     0L, 
                  TimeUnit.MILLISECONDS,
     new LinkedBlockingQueue<Runnable>(),
                          threadFactory);
}

如果向newFixedThreadPool线程池中提交的任务太多,可能会导致LinkedBlockingQueue非常大,从而出现OOM问题。

3.2 线程太多

Executors.newCachedThreadPool,它可以创建可缓冲的线程池,最大线程数量是Integer.MAX_VALUE,任务队列使用的是SynchronousQueue。

public static ExecutorService newCachedThreadPool() {
  return new ThreadPoolExecutor(0, 
                Integer.MAX_VALUE,
                               60L, 
                  TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>());
}

如果向newCachedThreadPool线程池中提交的任务太多,可能会导致创建大量的线程,也会出现OOM问题。

3.3 数据丢失 本文重点

如果线程池在执行过程中,服务突然被重启了,可能会导致线程池中的数据丢失。

上面的OOM问题,我们在日常开发中,可以通过自定义线程池的方式解决。

比如创建这样的线程池:

new ThreadPoolExecutor(8, 
                       10,
                       30L, 
     TimeUnit.MILLISECONDS,
    new ArrayBlockingQueue<Runnable>(300),
            threadFactory);

自定义了一个最大线程数量和任务队列都在可控范围内线程池。

这样做基本上不会出现OOM问题。

但线程池的数据丢失问题,光靠自身的功能很难解决。

3.3.1 如何保证数据不丢失?

线程池中的数据,是保存到内存中的,一旦遇到服务器重启了,数据就会丢失。

  • case 主流程依赖子流程 这种大部分业务场景要考虑任务丢失

image.png

  • case 主流程不依赖子流程 这种大部分业务场景不需要考虑任务丢失

image.png

  • 那么,如何保证数据不丢失呢?

  • 答:需要提前做持久化

我们优化的系统流程如下:

方案一

image.png

方案二

![image.png](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f1e36e1f5a0445a898f5a97461ecd5a6~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5ZCO56uv56iL5bqP5ZGYQXNrYQ==:q75.awebp?rk3s=f64ab15b&x-expires=1771823199&x-signature=AuZtNsZefMzpJQdVLgrqINiWlkI%3D)