最核心的任务提交方法是execute()方法,虽然通过submit也可以提交任务,但是实际上submit方法里面最终调用的还是execute()方法 并且ExecutorService中的invokeAll(),invokeAny()都是调用的execute方法。execute提交的任务无返回值,因此无法判断任务是否执行成功。但是如果出现线程错误可以显示部分异常堆栈信息
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); // 1.当前线程数量小于corePoolSize,则创建并启动线程。 int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) // 成功,则返回 return; c = ctl.get(); } // 2.步骤1创建线程失败,则尝试把任务加入阻塞队列, if (isRunning(c) && workQueue.offer(command)) { // 入队列成功,检查线程池状态,如果状态部署RUNNING而且remove成功,则拒绝任务 int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); // 如果当前worker数量为0,通过addWorker(null, false)创建一个线程,其任务为null else if (workerCountOf(recheck) == 0) addWorker(null, false); } // 3. 步骤1和2失败,则尝试将线程池的数量由corePoolSize扩充至maxPoolSize,如果失败,则拒绝任务 else if (!addWorker(command, false)) reject(command); } }
详细流程解读
workerCountOf方法根据ctl的低29位,得到线程池的当前线程数,如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务;否则执行步骤(2);
如果线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,则执行步骤(3),否则执行步骤(4)
再次检查线程池的状态,如果线程池没有RUNNING,且成功从阻塞队列中删除任务,则执行reject方法处理任务
执行addWorker方法尝试将线程池的数量由corePoolSize扩充至maxPoolSize,如果addWoker执行失败,则执行reject方法处理任务;
2.1.2 sumbit如果使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。可以通过以下方式改造submit获得部分异常堆栈信息
try { Future re = pools.submit(Task); re.get(); } catch (InterruptedException e) { // 处理中断异常 } catch (ExecutionException e) { // 处理无法执行任务异常 } finally { // 关闭线程池 executor.shutdown(); }
2.2 线程池的关闭使用线程池时,我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,但是它们的实现原理不同。
shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。