为什么要有线程池?
通过对线程的学习,我们在使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题,如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
此时就需要引入线程池的概念,在Java中可以通过线程池使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务。
在多线程编程中,合理利用线程池有三个好处:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
一、Java中的ThreadPoolExecutor类
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。我们先看看这个类的具体实现:
由源码我们可以看到ThreadPoolExecutor类继承自AbstractExecutorService类,并且提供了四种构造方法,前三种构造方法都是通过调用第四个构造方法进行的初始化操作。
public class ThreadPoolExecutor extends AbstractExecutorService{
//第一种
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
//第二种
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
//第三种
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
//第四种
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
解释下构造函数中各个参数的意义:
- corePoolSize:核心池的大小,池中所保存的线程数
- maximumPoolSize:线程池最大线程数
- keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
- unit:参数keepAliveTime的时间单位
- workQueue:一个阻塞队列,用来存储等待执行的任务
一般有以下选择:
(1) ArrayBlockingQueue:基于数组结构的有界阻塞队列
(2) LinkedBlockingQuene:基于链表结构的阻塞队列
(3) SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态
(4) priorityBlockingQuene:具有优先级的无界阻塞队列
- threadFactory:线程工厂,主要用来创建线程
- handler:表示当拒绝处理任务时的策略,四种
(1)ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
(2)ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
(3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
(4)ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
从上面的源码我们知道,ThreadPoolExecutor类继承AbstractExecutorService类,看下AbstractExecutorService类的具体实现
public abstract class AbstractExecutorService implements ExecutorService {
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value {
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
}
public Future<?> submit(Runnable task) {
}
public <T> Future<T> submit(Runnable task, T result) {
}
public <T> Future<T> submit(Callable<T> task) {
}
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException {
};
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
};
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
};
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
};
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
};
}
AbstractExecutorService是一个抽象类,它实现了ExecutorService接口。
再来看看ExecutorService接口的实现
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
而ExecutorService又是继承了Executor接口,我们看一下Executor接口的实现:
public interface Executor {
void execute(Runnable command);
}
到这里,就可以看出这四者之间的关系,
Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;
然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
然后ThreadPoolExecutor继承了类AbstractExecutorService。
下图可表示这种关系
二、线程池实现原理
1、线程池状态
//该状态表示线程池能接受新任务
private static final int RUNNING = -1 << COUNT_BITS;
//此状态不再接受新任务,但可继续执行队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//此状态全面拒绝,并中断正在处理的任务
private static final int STOP = 1 << COUNT_BITS;
//该状态表示所有任务已经被终止
private static final int TIDYING = 2 << COUNT_BITS;
//所有工作线程已经销毁,任务缓存队列已经清空或执行结束
private static final int TERMINATED = 3 << COUNT_BITS;
2、任务的执行
在ThreadPoolExecutor类中,最核心的任务提交方法是execute()方法,对其原理进行研究
public void execute(Runnable command) {
//判断提交的任务command是否为null,若是null,则抛出空指针异常;
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 如果当前线程数少于核心线程数,直接添加一个 worker 执行任务,
// 创建一个新的线程,并把当前任务 command 作为这个线程的第一个任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 当前线程数大于等于核心线程数,要么刚刚 addWorker 失败
// 如果线程池处于 RUNNING ,把这个任务添加到任务队列 workQueue 中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 若线程池不处于 RUNNING ,则移除已经入队的这个任务,并且执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 若线程池还是 RUNNING ,且线程数为 0,则开启新的线程
// 这块代码的真正意图:担心任务提交到队列中了,但是线程都关闭了
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 若 workQueue 满,到该分支
// 以 maximumPoolSize 为界创建新 worker,
// 若失败,说明当前线程数已经达到 maximumPoolSize,执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
这个过程可由下图表示
三、Executors静态工厂几种常用线程池
//创建固定容量大小的缓冲池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//创建容量为1的缓冲池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
//创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}