作用

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

Runnable VS Callable

  1. Runnable 接口不会返回结果或抛出检查异常,但是Callable 接口可以。
  2. 如果任务不需要返回结果或抛出异常推荐使用 Runnable 接口


execute() VS submit()区别

  • execute()用于提交不需要返回值的任务,所以无法判断这个任务是否被线程池执行成功与否
  • submit()用于提交需要返回值的任务。线程池返回一个future对象,通过这个对象来判断任务是否执行成功,并且可以通过 Futureget()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

创建线程池

《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors 返回线程池对象的弊端如下:

  • FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。

ThreadPoolExecutor类

  1. /**
  2. * 用给定的初始参数创建一个新的ThreadPoolExecutor。
  3. */
  4. public ThreadPoolExecutor(int corePoolSize,
  5. int maximumPoolSize,
  6. long keepAliveTime,
  7. TimeUnit unit,
  8. BlockingQueue<Runnable> workQueue,
  9. ThreadFactory threadFactory,
  10. RejectedExecutionHandler handler) {
  11. if (corePoolSize < 0 ||
  12. maximumPoolSize <= 0 ||
  13. maximumPoolSize < corePoolSize ||
  14. keepAliveTime < 0)
  15. throw new IllegalArgumentException();
  16. if (workQueue == null || threadFactory == null || handler == null)
  17. throw new NullPointerException();
  18. this.corePoolSize = corePoolSize;
  19. this.maximumPoolSize = maximumPoolSize;
  20. this.workQueue = workQueue;
  21. this.keepAliveTime = unit.toNanos(keepAliveTime);
  22. this.threadFactory = threadFactory;
  23. this.handler = handler;
  24. }

ThreadPoolExecutor 3 个最重要的参数:.

  • corePoolSize:核心线程数,定义了最小可以同时运行的线程数量
    • 刚初始化线程池时,不再立刻创建 corePoolSize 个工作线程,而是等待调用者不断提交任务的过程中,逐渐把工作线程 Worker 创建出来,等数量达到 corePoolSize 时就停止,把任务直接丢到队列里。
  • maxPoolSize:当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数
    • 当核心线程数和队列都满了时,新提交的任务仍然可以通过创建新的工作线程(叫它非核心线程),直到工作线程数达到 maximumPoolSize 为止
  • workQueue:当心任务来的时候会先判断当前运行的线程核心数量是否到达核心线程数,如果到达,新任务会被存放到队列。

ThreadPoolExecutor其他常见参数:

  • keepAliveTime:当线程池中的线程数量>corePoolSize的时候,如果这时候没有新任务提交,核心线程外的线程不回立即销毁,而是会等待,直到等待的时间超过这个时间才撤销
    • 非核心线程设定一个超时时间keepAliveTime,当这么长时间没能从队列里获取任务时,就不再等了,销毁线程。
  • unit:keepAliveTime的时间单位
  • threadFactory:executor创建新线程的时候会用到
  • handler:饱和策略

image.png

饱和策略

  • ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
  • ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。
  • ThreadPoolExecutor.DiscardPolicy 不处理新任务,直接丢弃掉。
  • ThreadPoolExecutor.DiscardOldestPolicy 此策略将丢弃最早的未处理的任务请求。