java提供了Executors线程池创建工具方便我们创建合适的线程池,示例如下,提供了四种创建线程池的简单方法,当然,其中有各自的优劣,之后 再叙述。
//创建一个会根据需要自动创建线程的线程池,并且有空闲线程存在时,不再创建新的线程,将重用该空闲线城池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//创建并保持固定数量的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
//利用主机上可用处理器的数量作为目标并行级别,创建一个基于工作-窃取算法的线程池
ExecutorService workStealingPool = Executors.newWorkStealingPool();
//创建一个线程池,可以调度命令在指定延迟后运行或周期性执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
接下来,根据上面java提供的简单方法创建一个CachedThreadPool简单的示例。
public class CachedTheadPoolDemo {
public static void main(String[] args){
// 创建线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 创建3个线程
for (int i = 0; i < 3; i++) {
CustomTask customTask = new CustomTask(i + "");
cachedThreadPool.execute(customTask);
}
}
}
// 自定义一个任务
class CustomTask extends Thread {
private String taskName;
public CustomTask(String taskName){
this.taskName = taskName;
}
@Override
public void run() {
// 循环打印出任务名和标签
for (int i = 0; i < 3; i++) {
System.out.println("Task: "+taskName+", Tag: "+i);
}
}
}
运行结果如下,
Task: 0, Tag: 0
Task: 1, Tag: 0
Task: 2, Tag: 0
Task: 1, Tag: 1
Task: 0, Tag: 1
Task: 1, Tag: 2
Task: 2, Tag: 1
Task: 0, Tag: 2
Task: 2, Tag: 2
在阿里巴巴java开发规范手册中,并不推荐使用Executors工具去创建线程池,而是使用ThreadPoolExecutor创建线程池,至于原因,在下面再讨论。那么,先看看ThreadPoolExecutor是怎么创建线程池的,示例如下
import java.util.concurrent.*;
public class ThreadPoolExecutorDemo {
public static void main(String[] args){
int corePoolSize = 4; // 一直保持在线程池内的线程数量,这个数量内的线程就算空闲也不会被回收,除非设置了allowCoreThreadTimeOut
int maximumPoolSize = 8; // 线程池允许持有的最大线程数量
long keepAliveTime = 10; // 当线程数量大于核心线程数量(corePoolSize)时,回收空闲线程的最大时间
TimeUnit unit = TimeUnit.SECONDS; // keepAliveTime的时间单位
BlockingQueue workQueue = new LinkedBlockingDeque<>(4); // 保存被执行前任务的队列,这个队列只会保存被execute方法提交的Runnable任务
ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 创建线程的工厂类
RejectedExecutionHandler rejectedPolicy = new ThreadPoolExecutor.AbortPolicy(); // 当线程阻塞并且达到任务队列最大容量时调用的处理器
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, rejectedPolicy);
for (int i = 0; i < 3; i++) {
CustomTask customTask = new CustomTask(i + "");
threadPoolExecutor.execute(customTask);
}
}
}
class CustomTask extends Thread {
private String taskName;
public CustomTask(String taskName){
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("Task: "+taskName+", Tag: "+i);
}
}
}
控制台输出结果,
Task: 2, Tag: 0
Task: 1, Tag: 0
Task: 0, Tag: 0
Task: 1, Tag: 1
Task: 2, Tag: 1
Task: 1, Tag: 2
Task: 0, Tag: 1
Task: 2, Tag: 2
Task: 0, Tag: 2
在第1节中,以及了解到了Executors线程池创建工具的简单使用,那么这一节将从源码的角度去看待各种创建方法的不同。
// 创建一个可以根据需要创建新线程的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
定义:Executors调用newCachedThreadPool()方法创建一个线程池,该线程池的核心线程数为0,线程池可持有的最大线程数为Integer类型的最大值,回收空闲线程的等待时间为60秒,采用阻塞队列保存任务(Task)。
// 创建一个保存固定线程数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
定义:Executors调用newFixedThreadPool(int nThreads)方法创建一个核心线程数量和线程数量相同的线程池,采用基于linked nodes的可选非阻塞队列保存任务,队列初始容量为Integer类型的最大值。
//用可用处理器数量作为目标并行级别创建一个work-stealing的线程池
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
定义: Executors调用newWorkStealingPool()方法创建一个线程池,该线程池为ForkJoinPool线程池,继承了AbstractExecutorService,使用DefaultForkJoinWorkerThreadFactory作为默认创建新线程的工厂类,默认对内部工作线程执行任务出现不可恢复错误时不做任何处理,并为未加入的forked task建立本地先进先出的任务调度模式。
// Executors中,传入核心线程数调用ScheduledThreadPool的构造函数,创建线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor中构造函数,创建一个线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
定义: Executors调用newScheduledThreadPool方法时,会间接调用ScheduledThreadPoolExecutor的构造函数创建线程池,该线程池核心线程数为corePoolSize,可持有的最大线程数为Integer类型的最大值,回收空闲线程的默认时间为10毫秒,采用一个专门的延迟队列,该队列也为阻塞队列,初始容量为16。