问题
这两天被一个问题折腾着:一个线程池,可能会有上万个任务要执行。问题是,一旦最先运行的线程执行完,整个线程池就结束了,哪怕队列里面还有很多任务还在等待着被执行。
有问题的代码
public static void main(String[] args){
List<String> datas = new ArrayList<>();
for (int i = 0; i < 15; i++){
datas.add(String.valueOf(i));
}
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
ExecutorService es = new ThreadPoolExecutor(2, 4,
0L, TimeUnit.MILLISECONDS, queue);
List<String> result = new ArrayList<>();
// Semaphore lock = new Semaphore(301);
for (String s : datas ) {
try {
// lock.acquire();
es.execute(() -> {
try {
//执行SQL: select sleep(1) num, name from user, 让该方法的调用暂停一下
TestThreadExecutor.getAll();
synchronized (result){
result.add(s);
}
} finally {
//lock.release();
System.out.println(">>>>>>" + s + "...queue>>>" + queue.size());
}
});
System.out.println("----------------->" + s + "...queue>>>" + queue.size());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("byebye~~~~~~~~~~~~~~");
es.shutdownNow();
try {
System.out.println("wait......");
es.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("datas size>>>" + datas.size() + "###result size>>>" + result.size() + "###queue size>>>" + queue.size());
}
先贴出jdk8里 shutdownNow() 与 shutdown() 方法的主要代码:
shutdownNow()
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
shutdown()
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
注意: shutdownNow()
方法的第四行drainQueue()
方法会遍历线程池的队列workQueue
,并将该队列中所有等待执行的任务全部remove掉。
drainQueue()
方法的代码:
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
总结
如果队列很大,不想执行完,只想让当前正在执行中的任务执行完就立马结束,可以调用shutdownNow()
方法。如果想要队列中任务全部都执行完再结束,那么调用shutdown()
方法。
后记
-
当时在网上copy了一段代码,代码里面是
shutdownNow()
方法,导致每次在刚执行完第一个任务时,整个线程池就结束了。因为这个原因折腾了两天,最后还是自己通过阅读JDK的代码才找到原因。 -
平时在用线程池的时候,直接用newFixedThreadPool、newCachedThreadPool或newSingleThreadExecutor就可以了,ThreadPoolExecutor偏底层了。
-
在查找问题的过程中,却搞明白了
Semaphore
的用法。用起来简单,用来做限流也很方便。