记录一次线上环境线程池使用后未关闭的记录

      下午时候运维的同学给我说 某个服务,线程池达到了2000 但是一直未回收,他们那边开始频繁报警了。然后用jstat导出了一份文件:

   记录一次线上环境线程池使用后未关闭的记录

   第一个线程池bing-master-order-1 占用了 1511个线程且未回收,第二个111 个。 

   刚开始自己也是一头雾水,看了下下面的 mysql/redis ,这个应该是线程池占用个数。但是第一个 是什么,找到项目 ,搜搜 bing-master-order 发现有下面的代码

   

            JSONObject jsonResult = JSONObject.parseObject(result);
            if (jsonResult.get(Constants.CODE) != null && jsonResult.getInteger(Constants.CODE) == 0) {
                logger.info("子单绑定主单成功");
                this.noticeAssign(orderNo);
                ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
                        new BasicThreadFactory.Builder().namingPattern("bing-master-order-%d").daemon(true).build());
                Future<String> future = executorService.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        logger.info("=================异步处理开始============" + jsonResult.getJSONObject(Constants.DATA));
                        JSONObject jsonData = jsonResult.getJSONObject(Constants.DATA);
                        try {
                            if (jsonData != null && jsonData.get(Constants.MAIN_ORDER_NO) != null) {
                                String newMainOrderNo = jsonData.getString("mainOrderNo");
                                MainOrderInterCity queryMain = interService.queryMainOrder(newMainOrderNo);
                                logger.info("========查询结果=======" + JSONObject.toJSONString(queryMain));
                                int code = 0;
                                if (queryMain != null && queryMain.getId() > 0) {
                                    code = interService.updateMainOrderState(newMainOrderNo, 1, dispatcherPhone);
                                } else {
                                    MainOrderInterCity main = new MainOrderInterCity();
                                    main.setDriverId(driverId);
                                    main.setCreateTime(new Date());
                                    main.setUpdateTime(new Date());
                                    main.setMainName(routeName);
                                    main.setStatus(MainOrderInterCity.orderState.NOTSETOUT.getCode());
                                    main.setMainOrderNo(newMainOrderNo);
                                    main.setOpePhone(dispatcherPhone);
                                    main.setMainTime(crossCityStartTime);
                                    code = interService.addMainOrderNo(main);
                                }
                                if (code > 0) {
                                    logger.info("=========子单绑定主单成功=======");
                                    return String.valueOf(code);
                                }
                                return String.valueOf(code);
                            }
                        } catch (Exception e) {
                            logger.error("子单绑定异常=======" + e);
                            String newMainOrderNo = jsonData.getString("mainOrderNo");
                            MainOrderInterCity queryMain = interService.queryMainOrder(newMainOrderNo);
                            logger.info("补录子单绑定开始====" + JSONObject.toJSONString(queryMain));
                            if (queryMain != null && queryMain.getId() > 0) {
                                int code = interService.updateMainOrderState(newMainOrderNo, 1, dispatcherPhone);
                                if (code > 0) {
                                    logger.info("=====补录异常成功=====");
                                }
                            }
                        }
                        return "=============子单绑定异常==========";
                    }
                });

                return AjaxResponse.success(null);


            } else {
                logger.info("子单指派主单返回信息========" + jsonResult.toString());
                return AjaxResponse.failMsg(jsonResult.getIntValue("code"), jsonResult.getString("msg"));
            }

        } 

        注意红色部分,点开.damon的源码

     

/**
         * Sets the daemon flag for the new {@code BasicThreadFactory}. If this
         * flag is set to <b>true</b> the new thread factory will create daemon
         * threads.
         *
         * @param f the value of the daemon flag
         * @return a reference to this {@code Builder}
         */
        public Builder daemon(final boolean f) {
            daemonFlag = Boolean.valueOf(f);
            return this;
        }

   如果为属性设置为true,表示将会创建一个daemon 守护线程。众所周知,垃圾回收的jvm就是守护进程,当jvm都是守护进程时候 jvm会自动退出,所以设置后一直没有被回收。知道原因后修改为false。想了下自己当时为何设置为true而不是false,应该是使用阿里规范代码时候推荐的。之前自己用的是ExcuterService 创建的,但是阿里规约不推荐,原因如下:

Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

当时就改成这样的了。没想到还是有风险,这次先这样给解决了,下次注意。