newFixedThreadPool线程池导致线程泄漏

  • 2020 年 1 月 21 日
  • 筆記

现象问题

最近看到线上的项目线程数过大的报警,开始还是不知道什么原因,因为很多项目都使用同样的线程池管理代码,认为那个项目是偶然的因素造成的,后来经过分析,发现线程数每天都在增加。其他的项目由于发布导致线程会从零开始计算,所以没有那么快达到报警值。 触发报警的代码大概如下:

boolean start=true;  public void doSomeThing(){      ExecutorService executorService = Executors.newFixedThreadPool(nThreads);      Thread thread = new Thread(() -> {          while (start) {              try {                  if(.//判断能否执行){                      Thread.sleep(100);                      return;                  }                  executorService.execute(() -> {                      try {                          //....your code                      } catch (Exception e) {                        } finally {                        }                  });              } catch (Exception e) {              }          }      });      thread.start();  }    public int getMaxThreadCount(){      return ....;  }  public void stop(){      this.start=false;  }

上面的代码存在两个问题:

  1. start是个主线程的变量,在主线程修改值,子线程的while循环不会停止 上述代码能够停止,因为在内部调用`Thread.sleep方法,导致线程内的变量刷新
  2. newFixedThreadPool 线程池没有调用shutdown方法,导致线程不会被回收。

改正方法:

  1. start 设置成线程共享变量volatile类型
  2. 在最后调用停止的时候,让线程池进行回收

修改后代码如下:

volatile boolean start=true;  ExecutorService executorService;  Thread thread ;  public void doSomeThing(){      executorService = Executors.newFixedThreadPool(nThreads);      thread = new Thread(() -> {          while (start) {              try {                   if(.//判断能否执行){                      Thread.sleep(100);                      return;                  }                  executorService.execute(() -> {                      try {                          //....your code                      } catch (Exception e) {                        } finally {                        }                  });              } catch (Exception e) {              }          }      });      thread.start();  }    public int getMaxThreadCount(){      return ....;  }  public void stop(){      this.start=false;      if (executorService != null) {          executorService.shutdown();          executorService.awaitTermination(MaxConcurrency() * 3, TimeUnit.SECONDS);          for (int i = 0; i < 10 && !executorService.isTerminated(); i++) {              Thread.sleep(1000);          }      }      if (thread != null && thread.isAlive()) {          thread.interrupt();          thread.join(2000);      }  }

最后的疑问

线程池在最后不使用后,为什么线程没有被释放?GC为什么没有把线程池对象回收?是怎么做到的?

目前还没有找到问题的答案,等找到后回来更新。