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


    在上篇博客中,我尝试通过一个简单的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的产生。

  • 相关阅读:
    软件测试系列--集成测试
    软件测试系列--系统测试
    软件测试系列-软件质量
    Java初认识--函数和数组
    Java初认识--Java中的语法结构
    Java初认识--Java语言的书写规范及基本的运算符
    Java初认识--环境搭建及基本数据类型
    软件测试系列--软件缺陷管理
    ASP.NET MVC 路由系统类
    由浅入深了解Retrofit(一)
  • 原文地址:https://www.cnblogs.com/cy1995/p/13295230.html
Copyright © 2020-2023  润新知