Java的ExecutorService的shutdownNow()方法并不能保证一定会结束线程的解决办法
这几天使用ExecutorService的时候遇到了在Task内部进行读取文件操作而导致死循环的问题,当我试图调用shutdownNow()方法的时候,发现并不是像我预想的一样会理解结束线程。我在JDK的API文档中看到了相关解释,Oracle官方的解释是并不保证会结束线程。原文在此:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
我这里引述一下,注意红色部分:
-
shutdownNow
List<Runnable> shutdownNow()
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.This method does not wait for actively executing tasks to terminate. Use
awaitTermination
to do that.There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via
Thread.interrupt()
, so any task that fails to respond to interrupts may never terminate.- Returns:
- list of tasks that never commenced execution
- Throws:
SecurityException
- if a security manager exists and shutting down this ExecutorService may manipulate threads that the caller is not permitted to modify because it does not holdRuntimePermission
("modifyThread")
, or the security manager'scheckAccess
method denies access.
我搜索了之后,发现国外有个老哥实现了一个ExecutorService貌似可以结束线程,但是我没有仔细看,我稍微修改了一下,让它能够在我的项目中跑起来,目前看没有什么问题,
国外老哥的代码在这里:java - How to force terminate all workers in ThreadPoolExecutor immediately - Stack Overflow
我略微修改了一下,代码如下:
package com.xxx.api.utils; import cn.hutool.core.thread.ThreadFactoryBuilder; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class KillableThreadPoolExecutor extends ThreadPoolExecutor { private final Map<Runnable, Thread> executingThreads; public KillableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, String threadNamePrefix) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingDeque<Runnable>(), ThreadFactoryBuilder.create().build()); executingThreads = new HashMap<>(maximumPoolSize); } @Override protected synchronized void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); executingThreads.put(r, t); } @Override protected synchronized void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if(executingThreads.containsKey(r)) { executingThreads.remove(r); } } @Override public synchronized List<Runnable> shutdownNow() { List<Runnable> runnables = super.shutdownNow(); for(Thread t : executingThreads.values()) { t.stop(); } return runnables; } }
使用:
KillableThreadPoolExecutor killableThreadPoolExecutor = new KillableThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, ""); FutureTask<?> futureTask = new FutureTask(() -> { ResultEntity docTmp = null; //耗时操作可能会死循环,所以放到线程中进行监控 docTmp = new ResultEntity(inPathStream); return docTmp; }); Future<?> submit = killableThreadPoolExecutor.submit(futureTask); try { //System.out.println(DateUtil.now()); //等待20s,否则认为超时,死循环了 Object obj = futureTask.get(20, TimeUnit.SECONDS); // System.out.println(DateUtil.now()); if (null == obj) { //超时 pdfPageCount = 0; } else { doc = (ResultEntity) obj; //页数 pdfPageCount = doc.getPageCount(); } } catch (TimeoutException e) { killableThreadPoolExecutor.shutdownNow(); } catch (Exception e) { killableThreadPoolExecutor.shutdownNow(); } finally { //System.out.println(DateUtil.now()); killableThreadPoolExecutor.shutdownNow(); }
////////////////////////////////
////////Sixi. Let it be.../////
//////////////////////////////