下午时候运维的同学给我说 某个服务,线程池达到了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。
当时就改成这样的了。没想到还是有风险,这次先这样给解决了,下次注意。