使用线程池过程中肯能遇到的问题

在上篇博客中,我尝试通过一个简单的Demo介绍了一下线程池的构造参数和工作过程,这一篇博客则会继续探讨一下在使用线程池过程中可能遇到的问题。

1.线程池使用时需要遵守的规范

在阿里的Java的开发手册中对于线程池的使用用如下几条规范

  1. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
  2. 【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
  3. 【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这 样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    • 说明:Executors返回的线程池对象的弊端如下: 1) FixedThreadPool和SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

关于规范的前两点很好理解,接下来我们着重来看一下第三点。

  • 使用newFixedThreadPool()构建线程池
@GetMapping("/oom1")
    public void oom1() throws InterruptedException {
        
        ThreadPoolExecutor threadPool = ((ThreadPoolExecutor) Executors.newFixedThreadPool(1));
        printfStatus(threadPool);

        for (int i = 0; i < 100000000; i++) {
            threadPool.execute(() -> {
                String payload = IntStream.rangeClosed(1, 1000000)
                        .mapToObj(__ -> "a")
                        .collect(Collectors.joining("")) + UUID.randomUUID().toString();

                try {
                    TimeUnit.HOURS.sleep(1);
                } catch (InterruptedException e) {
                    log.info(payload);
                }
            });
        }

        threadPool.shutdown();
        threadPool.awaitTermination(1, TimeUnit.HOURS);
    }

    private void printfStatus(ThreadPoolExecutor threadPool) {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            log.info("======================");
            log.info("Pool Size: {}", threadPool.getPoolSize());
            log.info("Active Threads:{}", threadPool.getActiveCount());
            log.info("Number of Tasks Completed:{}", threadPool.getCompletedTaskCount());
            log.info("Number of Task in Queue:{}", threadPool.getQueue().size());
            log.info("=====================");

        }, 0, 1, TimeUnit.SECONDS);
    }

运行结果

Exception in thread "http-nio-8080-exec-1" Exception in thread "http-nio-8080-exec-2" java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Java heap space

很显然在我们对Executors.newFixedThreadPool(1))创建的单线程的线程池传入大量任务时出现了OutOfMemoryError,至于为什么会出现这个问题我们可以看一下newFixedThreadPool的源码

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

在构建FixedThreadPool时,任务队列采用的是LinkedBlockingQueue,而LinkedBlockingQueue是一个Inter.MAX_VALUE长度的队列,由于超过核心线程数的任务会被保存在任务队列之中,因此在传入大量任务后会导致大量任务堆积在LinkedBlockingQueue的任务队列之中,从而导致OOM的产生。

posted @ 2020-07-13 19:39  三弦音无  阅读(419)  评论(0编辑  收藏  举报