一、线程池关闭问题
1、我们知道下面这段代码,线程池作为局部变量使用,是没有意义的。每次进来请求都是创建一个线程池后,销毁线程,关闭线程池。然后在来一个请求,再次创建一个线程池,在关闭线程。
@RequestMapping("/test")
public String test() {
ThreadPoolExecutor executorService = new ThreadPoolExecutor(5, 5,
10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(50));
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId());
}
});
executorService.shutdown();
return "hello world";
}
2、现在我们把线程池设置为成员变量,我们看到方法的最后关闭了线程executorService.shutdown() 但是关闭后就不能有新的请求过来了, 如果我们不关闭,线程会一直存活,最后随着请求的增多会造成OOM
private ThreadPoolExecutor executorService = new ThreadPoolExecutor(5, 5,
10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(50));
@RequestMapping("/test")
public String test() {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId());
}
});
executorService.shutdown();
return "hello world";
}
二、如何优雅的关闭线程呢?
自定义线程池,并设置keepAliveTime线程的存活时间
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
1、存活时间到了后,只关闭超出核心线程的线程
这里我们设置核心线程1个,最多线程5个,存活时间10s, 为了演示超出核心线程再去创建线程,队列设置1
private ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 5,
10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1));
@RequestMapping("/test")
public String test() {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId());
}
});
return "hello world";
}
下图可以看到一直存活的线程是核心线程。超出核心线程以外的线程等到10s后就销毁了
2、存活时间到了后,销毁所有线程(包括核心线程)
这里我们executorService.allowCoreThreadTimeOut(true) 设置后,存活时间到了后会销毁所有线程
private ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 5,
10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1));
@RequestMapping("/test")
public String test() {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId());
}
});
executorService.allowCoreThreadTimeOut(true);
return "hello world";
}
下图可以看到所有线程都销毁了