33. 通过Executors创建线程池
Executors及其服务的类
java.util.concurrent.Executors
是JDK的并发包下提供的一个工厂类(Factory)和工具类(Utility)。
Executors
提供了关于Executor
, ExecutorService
, ScheduledExecutorService
, ThreadFactory
和 Callable
相关的工厂方法和工具方法。
Executor
是一个执行提交的Runnable Tasks
的对象,它有一个execute
方法,参数是Runnable
。当执行execute
方法以后,会在未来某个时间,通过创建线程或者使用线程池中的线程的方式执行参数中的任务。用法如下:
Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
ExecutorService
继承了Executor
,并提供了更多有意思的方法,比如shutdown
方法会让ExecutorService
拒绝创建新的线程来执行task。
Executors常用的几个方法
//创建固定线程数量的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
//创建一个线程池,该线程池会根据需要创建新的线程,但如果之前创建的线程可以使用,会重用之前创建的线程
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//创建一个只有一个线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
一个线程池的例子
下面我就创建5个Task,并通过一个包含3个线程的线程池来执行任务。我们一起看下会发生什么。
ThreadPoolExample1
就是我们的测试类,下面所有的内部类、常量和方法都写在这个测试类里。
package net.ijiangtao.tech.concurrent.jsd.threadpool;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample1 {
}
任务
Task
内部类执行了两次for循环,并在每次循环执行结束以后 sleep 1秒钟。
// Task class to be executed (Step 1)
static class Task implements Runnable {
private String name;
public Task(String s) {
name = s;
}
// Prints task name and sleeps for 1s
// This Whole process is repeated 2 times
public void run() {
try {
for (int i = 0; i <= 1; i++) {
if (i == 0) {
//prints the initialization time for every task
printTimeMsg("Initialization");
} else {
// prints the execution time for every task
printTimeMsg("Executing");
}
Thread.sleep(1000);
}
System.out.println(name + " complete");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void printTimeMsg(String state) {
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println(state+" Time for"+ " task name - " + name + " = " + ft.format(d));
}
}
池子
创建一个固定线程数的线程池。
// Maximum number of threads in thread pool
static final int MAX_T = 3;
// creates a thread pool with MAX_T no. of
// threads as the fixed pool size(Step 2)
private static final ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
测试
创建5个任务,并通过线程池的线程执行这些任务。
public static void main(String[] args) {
// creates five tasks
Runnable r1 = new Task("task 1");
Runnable r2 = new Task("task 2");
Runnable r3 = new Task("task 3");
Runnable r4 = new Task("task 4");
Runnable r5 = new Task("task 5");
// passes the Task objects to the pool to execute (Step 3)
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
pool.execute(r4);
pool.execute(r5);
// pool shutdown ( Step 4)
pool.shutdown();
}
执行结果如下。
Initialization Time for task name - task 1 = 12:39:44
Initialization Time for task name - task 2 = 12:39:44
Initialization Time for task name - task 3 = 12:39:44
Executing Time for task name - task 3 = 12:39:45
Executing Time for task name - task 1 = 12:39:45
Executing Time for task name - task 2 = 12:39:45
task 2 complete
Initialization Time for task name - task 4 = 12:39:46
task 3 complete
Initialization Time for task name - task 5 = 12:39:46
task 1 complete
Executing Time for task name - task 5 = 12:39:47
Executing Time for task name - task 4 = 12:39:47
task 5 complete
task 4 complete
说明
从输出的结果我们可以看到,5个任务在包含3个线程的线程池执行。
- 首先会有3个任务(task 1,task 2,task 3)获得线程资源并发执行;
- (task 2)执行成功以后,让出线程资源,(task 4)开始执行;
- (task 3)执行成功以后,让出线程资源,(task 5)开始执行;
- 最终,5个任务都执行结束,线程池将线程资源回收。
由于线程的执行有一定的随机性,以及不同机器的资源情况不同,每次的执行结果,可能会有差异。
下面是我第二次执行的结果。
Initialization Time for task name - task 1 = 12:46:33
Initialization Time for task name - task 3 = 12:46:33
Initialization Time for task name - task 2 = 12:46:33
Executing Time for task name - task 2 = 12:46:34
Executing Time for task name - task 3 = 12:46:34
Executing Time for task name - task 1 = 12:46:34
task 3 complete
task 2 complete
task 1 complete
Initialization Time for task name - task 4 = 12:46:35
Initialization Time for task name - task 5 = 12:46:35
Executing Time for task name - task 4 = 12:46:36
Executing Time for task name - task 5 = 12:46:36
task 5 complete
task 4 complete
task 1 2 3 获得线程资源,task 4 5排队等待:

task 1 2 3 执行结束,task 4 5获得线程资源,线程池中有一个线程处于空闲状态:

但规律是相同的,那就是线程池会将自己的线程资源贡献出来,如果任务数超出了线程池的线程数,就会阻塞并排队等待有可用的线程资源以后执行。
也就是线程池会保证你的task在将来(Future)的某个时间执行,但并不能保证什么时间会执行。
相信你现在对于ExecutorService
的invokeAll
方法,可以执行一批task并返回一个Future
集合,就会有更深入的理解了。
List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException
通过ExecutorService
线程池执行task的过程如下图所示,超出线程池线程数量的task将会在BlockingQueue
排队等待获得线程资源的机会。

总结
本教程带领大家了解了线程池的来由、概念和基本用法,相信大家看完,以后就不再只会傻傻地new Thread
了。