• 如果当前线程数量小于corePoolSize,新任务会创建一个新的线程去执行;
  • 如果当前线程数量大于corePoolSize,小于maximumPoolSize,新任务会加入队列当中,即便存在空闲的线程;

线程池状态

线程池包括以下几种状态:

  • RUNNING:接收新任务并且可以处理队列中的任务;
  • SHUTDOW:不接受新任务,但是可以处理队列中的任务;
  • STOP:不接受新任务、不处理队列中的任务并且中断处理中的任务;
  • TIDYING:所有任务已经终止,线程数量为0,所有线程将执行terminated()方法;
  • TERMINATED:terminated()方法已经执行完成。

问题

  • 提交任务包括execute和submit两种方法,这两个有什么区别?

两个方法都可以提交任务,submit可以获取到任务执行的结果,execute方法无返回值。

  • 线程池shutDown和shutDownNow两个方法的区别

这两个方法都可以终止线程池,不再接受新的任务。但是shutDown会等所有正在执行和任务队列中的任务执行完成,但是shutDownNow立刻终止线程池,线程池任务队列中的任务不会被执行,正在被执行的任务会被立刻终止,抛出线程被中断的异常:java.lang.InterruptedException: sleep interrupted。

线程池任务执行流程

  • 为什么设置核心线程数和最大线程数
    • 当任务队列满了之后,才会创建新的线程;
    • 业务有高峰和低谷,在业务低谷期,没必要维护这么多的线程数,线程的切换也会耗费CPU资源。
  • 队列都是有序的吗?

    • 队列包括LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue、LinkedTransferQueue等,像链表和数组队列都是有序的,优先级队列会按照任务的优先级入队,是无序的。
  • 线程池为什么使用阻塞队列?

使用阻塞队列可以减少对CPU资源的消耗。当线程等待新的任务时,如果是非阻塞队列,会一直遍历队列,直到队列中有新的任务。而非阻塞队列会放弃CPU资源,直到有新的任务到来。

  1. try {
  2. Runnable r = timed ?
  3. workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
  4. workQueue.take();
  5. if (r != null)
  6. return r;
  7. timedOut = true;
  8. } catch (InterruptedException retry) {
  9. timedOut = false;
  10. }

任务提交源码分析

  1. public void execute(Runnable command) {
  2. if (command == null)
  3. throw new NullPointerException();
  4. /*
  5. * Proceed in 3 steps:
  6. *
  7. * 1. If fewer than corePoolSize threads are running, try to
  8. * start a new thread with the given command as its first
  9. * task. The call to addWorker atomically checks runState and
  10. * workerCount, and so prevents false alarms that would add
  11. * threads when it shouldn't, by returning false.
  12. *
  13. * 2. If a task can be successfully queued, then we still need
  14. * to double-check whether we should have added a thread
  15. * (because existing ones died since last checking) or that
  16. * the pool shut down since entry into this method. So we
  17. * recheck state and if necessary roll back the enqueuing if
  18. * stopped, or start a new thread if there are none.
  19. *
  20. * 3. If we cannot queue task, then we try to add a new
  21. * thread. If it fails, we know we are shut down or saturated
  22. * and so reject the task.
  23. */
  24. int c = ctl.get();
  25. //1.判断当前活跃线程数是否大于corePoolSize,如果小于corePoolSize,新增一个线程,否则尝试放入队列
  26. if (workerCountOf(c) < corePoolSize) {
  27. if (addWorker(command, true))
  28. return;
  29. c = ctl.get();
  30. }
  31. //2.1.如果线程池是RUNNING状态,那么将任务放入队列。此时有个二次确认,判断线程池状态,防止在这个过程中线程池已经STOP,无法执行
  32. //入队的任务;
  33. //2.2 这里还会判断活跃的线程,如果此时没有可用的线程,那么需要新增一个线程。
  34. if (isRunning(c) && workQueue.offer(command)) {
  35. int recheck = ctl.get();
  36. if (! isRunning(recheck) && remove(command))
  37. reject(command);
  38. else if (workerCountOf(recheck) == 0)
  39. addWorker(null, false);
  40. }
  41. //3.如果队列已满,那么使用最大线程池。否则的话,执行拒绝策略。
  42. else if (!addWorker(command, false))
  43. reject(command);
  44. }