什么是线程池
我们在应用中,通过new Thread().start()的方法创建执行一个线程来执行任务,执行完后线程关闭,整个过程中,线程的创建和关闭需要花费时间,当线程数量多的时候,会占用很多CPU资源。所以,为了减少频繁创建和关闭线程的开销。我们可以让创建好的线程复用。如同数据库连接池,我们在进行数据库的查询的时候,需要建立连接和销毁连接,为了避免连接和销毁的开销,我们可以通过数据库连接池(如c3p0,druid等)来管理数据库连接。线程池也是这样,在“池”中,有可用的线程,当需要线程来执行任务时候,直接从线程池中取,执行完后,不用关闭,可以留着给新的线程复用。
线程池的实现
下面是ThreadPoolExecutor的UML类图。
顶层接口是Executor
只有一个方法execute()
public interface Executor {
//定义 execute 方法来执行任务
void execute(Runnable command);
}
public interface Executor {
//定义 execute 方法来执行任务
void execute(Runnable command);
}
ExecutorService接口
对Executor进行了扩展,丰富了对任务的执行和管理的功能。提供了管控线程池的方法,比如停止线程池的运行;还提供了给一个或多个异步任务生成Future的方法,
// 关闭线程池
void shutdown();
//是否已经关闭,true 已关闭,false 未关
boolean isShutdown();
// 所有的任务是否都已经终止
boolean isTerminated();
//请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// 提交一个 Runnable 有返回值的任务用于执行,并返回一个表示该任务的 Future。
Future submit(Callable task);
// 提交没有返回值的任务
Future> submit(Runnable task);
//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。 List> invokeAll(Collection extends Callable> tasks)
throws InterruptedException;
// 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。其余未完成的任务将被取消 T invokeAny(Collection extends Callable> tasks)
throws InterruptedException, ExecutionException;
// 关闭线程池
void shutdown();
//是否已经关闭,true 已关闭,false 未关
boolean isShutdown();
// 所有的任务是否都已经终止
boolean isTerminated();
//请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// 提交一个 Runnable 有返回值的任务用于执行,并返回一个表示该任务的 Future。
Future submit(Callable task);
// 提交没有返回值的任务
Future> submit(Runnable task);
//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。 List> invokeAll(Collection extends Callable> tasks)
throws InterruptedException;
// 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。其余未完成的任务将被取消 T invokeAny(Collection extends Callable> tasks)
throws InterruptedException, ExecutionException;
AbstractExecutorService抽象类
实现了ExecutorService接口的一些方法如 submit、invokeAny、invokeAll 等方法,使得在使用线程池的时候,只用关注任务的实现,将任务提交给线程池来处理就行了。execute方法并没有实现,这个交由ThreadPoolExecutor了
实现类ThreadPoolExecutor
这是线程池的核心类,它维护了线程池的生命周期和核心参数。下面再详细说明。
ForkJoinPool
ForkJoinPool是Java7提供的,可以用来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果的分而治之框架。
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor继承了ScheduledThreadPoolExecutor,通过实现ScheduledExecutorService接口,对ThreadPoolExecutor进行了扩展,支持延时后执行异步任务或者周期性执行任务。
线程池核心ThreadPoolExecutor
线程池的状态
线程池内部维护了自己的生命周期,它将运行状态(rs)和线程数量 (wc)两个关键参数的维护放在了一起 通过一个 32 位的原子整数来表示。其中高 3 位表示rs,低 29 位表示wc
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//32-3=29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 29个1
private static final int CAPACITY = (1 <
// runState is stored in the high-order bits
private static final int RUNNING = -1 < private static final int SHUTDOWN = 0 < private static final int STOP = 1 < private static final int TIDYING = 2 < private static final int TERMINATED = 3 <
//计算当前运行状态,将c 的低 29 位修改为 0,就得到了线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//计算当前线程数量:将整数 c 的高 3 为修改为 0,可以得到线程池中的线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//32-3=29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 29个1
private static final int CAPACITY = (1 <
// runState is stored in the high-order bits
private static final int RUNNING = -1 < private static final int SHUTDOWN = 0 < private static final int STOP = 1 < private static final int TIDYING = 2 < private static final int TERMINATED = 3 <
//计算当前运行状态,将c 的低 29 位修改为 0,就得到了线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//计算当前线程数量:将整数 c 的高 3 为修改为 0,可以得到线程池中的线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
状态 | 描述 |
RUNNING | 接受新的任务,处理workQueue中的任务 |
SHUTDOWN | 不接受新的任务提交,但会处理workQueue中的任务 |
STOP | 不接受新的任务提交,不再处理workQueue中的任务,中断正在执行任务 |
TIDYING | 所有的任务都终止了,workCount 为 0 |
TERMINATED | terminated() 方法结束后,线程池的状态就会变成这个状态 |
- RUNNING -> SHUTDOWN:当调用了 shutdown() 后,会发生这个状态转换
- RUNNING -> STOP 调用 shutdownNow()
- SHUTDOWN -> TIDYING:当任务队列和线程池都清空后。
- STOP -> TIDYING:任务队列清空后,线程池中工作线程数量为0,发生这个转换
- TIDYING -> TERMINATED:在terminated()方法执行完后
线程池的核心参数
参数 | 描述 |
corePoolSize | 核心线程数 |
maximumPoolSize | 最大线程数,线程池允许创建的最大线程数 |
workQueue | 任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务 |
keepAliveTime | 线程池中的线程空闲时间。 |
unit | keepAliveTime 参数的时间单位。 |
threadFactory | 执行程序创建新线程时使用的工厂。 |
handler | 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。 |
线程池运行规则
- 如果 ==线程数 < corePoolSize== ,则创建并启动一个线程来执行新提交的任务
- 如果线程数 >= corePoolSize && 线程数
- 如果workQueue已满,并且线程数
- 如果workQueue已满,并且线程数>=maximumPoolSize,则拒绝任务。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 如果线程数if (workerCountOf(c) if (addWorker(command, true))return;
c = ctl.get();
}//如果线程数>=corepoolsize,线程池处于 RUNNING 状态,把这个任务添加到workQueue 中if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();// 如果这时候线程池不 RUNNING了,就移除已经入队的这个任务,并执行拒绝策略if (! isRunning(recheck) && remove(command))
reject(command);else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}// 如果 workQueue 队列满了,线程数// 如果失败,说明线程数已经达到 maximumPoolSize,执行拒绝策略else if (!addWorker(command, false))
reject(command);
}
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 如果线程数if (workerCountOf(c) if (addWorker(command, true))return;
c = ctl.get();
}//如果线程数>=corepoolsize,线程池处于 RUNNING 状态,把这个任务添加到workQueue 中if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();// 如果这时候线程池不 RUNNING了,就移除已经入队的这个任务,并执行拒绝策略if (! isRunning(recheck) && remove(command))
reject(command);else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}// 如果 workQueue 队列满了,线程数// 如果失败,说明线程数已经达到 maximumPoolSize,执行拒绝策略else if (!addWorker(command, false))
reject(command);
}
创建线程池
通过Executors类提供的工厂方法可以创建线程池。
- Executors.newWorkStealingPool创建持有足够线程的线程池,支持给定并行度。创建ForkJoinPool。Java8引入。
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
//Runtime.getRuntime().availableProcessors获取CPU数量,作为线程池的并行度
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
//Runtime.getRuntime().availableProcessors获取CPU数量,作为线程池的并行度
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
- Executors.newCachedThreadPool 需要的时候创建新的线程,同时可以复用之前创建的线程的线程池
- Executors.newScheduledThreadPool 支持定时和周期性执行的线程池,与newCachedThreadPool的区别在于它不回收工作线程
- Executors.newSingleThreadExecutor 生成单线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
- Executors.newFixedThreadPool 一个固定大小的线程池 ,它的核心线程数=最大线程数,不存在空闲线程。keepAliveTime=0。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
线程池的拒绝策略
当线程池的任务缓存队列满了,且线程池中的线程数目达到maximumPoolSize时,就需要拒绝掉该任务了。
- AbortPolicy 线程池的默认拒绝策略,丢弃任务,抛出RejectedExecutionException。
- DiscardPolicy 丢弃任务,不抛异常
- DiscardOldestPolicy 丢弃任务队列最早的任务,重新提交当前任务
- CallerRunsPolicy 只要线程池没有被关闭,那么由提交任务的线程自己来执行这个任务。
钩子方法
addWorker 方法的功能就是增加一个线程, 使用 new Worker (firstTask) 新建Worker,再使用 start () 执行 ,其实调用到的是runWorker()方法。
runWorker(Worker w)
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
//help gc
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
//锁住 worker
w.lock();
// 线程池 stop 中,但是线程没有到达中断状态,帮助线程中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//执行 before 钩子函数
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行任务
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//执行 after 钩子函数
afterExecute(task, thrown);
}
} finally {
//任务执行完成,计算解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
//help gc
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
//锁住 worker
w.lock();
// 线程池 stop 中,但是线程没有到达中断状态,帮助线程中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//执行 before 钩子函数
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行任务
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//执行 after 钩子函数
afterExecute(task, thrown);
}
} finally {
//任务执行完成,计算解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
在runWorker()方法中,ThreadPoolExecutor提供了两个钩子函数beforeExecute(wt, task)和afterExecute(task, thrown);其实还有一个terminated()。我们可以利用这几个钩子方法扩展我们的线程池。
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0l,
TimeUnit.SECONDS, new LinkedBlockingQueue()) {@Overrideprotected void beforeExecute(Thread t, Runnable r) {
System.out.println("开始执行:" + r);
}@Overrideprotected void afterExecute(Runnable r, Throwable t) {
System.out.println("执行完成:" + r);
}@Overrideprotected void terminated() {
System.out.println("Thread Pool terminated!");
}
};
executorService.submit(() -> {
System.out.println("Task Running!!!");
});
executorService.shutdown();
}
}
执行结果
开始执行:java.util.concurrent.FutureTask@212aa004
Task Running!!!
执行完成:java.util.concurrent.FutureTask@212aa004
Thread Pool terminated!
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0l,
TimeUnit.SECONDS, new LinkedBlockingQueue()) {@Overrideprotected void beforeExecute(Thread t, Runnable r) {
System.out.println("开始执行:" + r);
}@Overrideprotected void afterExecute(Runnable r, Throwable t) {
System.out.println("执行完成:" + r);
}@Overrideprotected void terminated() {
System.out.println("Thread Pool terminated!");
}
};
executorService.submit(() -> {
System.out.println("Task Running!!!");
});
executorService.shutdown();
}
}
执行结果
开始执行:java.util.concurrent.FutureTask@212aa004
Task Running!!!
执行完成:java.util.concurrent.FutureTask@212aa004
Thread Pool terminated!
使用线程池的注意点
- 线程池的大小会对应用的性能产生影响。因此需要合理的设置线程池参数
- FixedThreadPool 和 SingleThreadPool允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。CachedThreadPool 和 ScheduledThreadPool 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。基于以上原因,在阿里巴巴Java开发手册中强制要求线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。