Java并发之线程池的使用浅析(2)

最核心的任务提交方法是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,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d361b8ecd1f70068552bee0deb3f2ba9.html