2018.10.29
前言
并发问题是项目开发中的常见问题,Java的java.util.concurrent
包提供了许多可靠、实用的工具给开发者。粗略地看了文档和代码,决定写下这篇简介,但是出于时间成本的考虑,本文未涵盖代码案例及测试分析等内容。
万恶之源的Executor
接口
下图是Executor
接口及其相关接口、类的UML图1。Executor
作为最顶层的接口,顾名思义就是执行某个可执行命令(Runnable
线程)的执行器。
ExecutorService
接口则是在Executor
的基础上,提供这个可执行命令的管理方法,主要是启停操作。到此,ExecutorService
接口满足了对线程生命周期基本的管理需求,调度相关的需求由ScheduledExecutorService
接口来补充。
AbstractExecutorService
抽象类是ExecutorService
的默认实现。子类ForkJoinPool
的特点是支持任务窃取机制(Work-Stealing),对于那些大量产生子任务的任务,该子类采用的任务窃取机制会有助于提高整体的性能表现。而本文的主角ThreadPoolExecutor
类,作为一个线程池则适用于大量异步任务执行的场景,具体原因有待实验验证。
Executors
血汗工厂
使用Java的Executors
工厂方法创建ExecutorService
实例,可以减少开发者对参数的理解成本和选型成本。常用的有如下几个:
newSingleThreadExecutor
:是一个只有一个线程的线程池,使用上,就是一个可自主管控的线程,可直接控制启停、判断状态等。newFixedThreadPool
:固定大小的线程池,当需要控制并发度或在并发度不高的场景下,就可以采用该线程池。newCachedThreadPool
:缓存线程池,这里的缓存是指当线程执行完任务之后,还会留在线程池中,对于短寿异步任务较多的场景,这个线程池能带来可观的性能提升;而且线程池大小不受限,最大能包含Integer.MAX_VALUE
个线程。
以上三个线程池本质上都是ThreadPoolExecutor
,只是配置不同。而还有其它线程池,如基于ForkJoinPool
的newWorkStealingPool
,不作过多介绍,读者可按需深入了解。