有哪些常见线程池类型
以使用工具类 Executors 进行创建线程池为参照
// FixedThreadPool 固定线程数池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
// SingleThread 单线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// CachedThreadPool 缓存线程效果的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// ScheduledThreadPool 可延迟或定期执行的线程池
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
// 线程可以窃取其他线程的队列任务进行执行
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
// 不可配置的线程池
public static ExecutorService unconfigurableExecutorService(ExecutorService executor) {
if (executor == null)
throw new NullPointerException();
return new DelegatedExecutorService(executor);
}
FixedThreadPool、SingleThread、CachedThreadPool、ScheduledThreadPool 底层均是 ThreadPoolExecutor 类创建的对象。
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);// 线程保活时间,可设置 allowCoreThreadTimeOut 状态,使其对核心线程也进行回收。
this.threadFactory = threadFactory;//创建线程的工厂
this.handler = handler;//拒绝服务策略
}
构造器方法,说明线程池刚创建时,不创建线程对象,等到执行任务时才创建线程。
进入到执行任务的核心方法 execute:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps: 分三步处理
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first 如果运行的线程少于corePoolSize,那么尝试使用给定命令作为第一个任务启动一个新线程。
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false. 对addWorker的调用会自动检查runState和workerCount,从而通过返回false防止在不应该添加线程的情况下添加线程的错误警报。
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that 如果任务能成功排队,
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new 如果不能排队成功,则尝试添加一个新的线程,如果失败说明线程关闭或线程池耗尽,拒绝任务
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {//工作线程数量小于核心线程
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { //将执行任务添加到工作队列中
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)//工作线程数量为0
addWorker(null, false);
}
else if (!addWorker(command, false)) //核心线程耗尽且队列满了才进行创建新的非核心线程
reject(command);//创建失败,走拒绝策略
}
先创建核心线程 -> 核心线程数满了则放到工作队列中 -> 工作队列也满了则创建非核心线程。
核心线程数满了,工作队列满了,非核心线程满了,则走拒绝策略。有如下几种拒绝策略。
// 丢弃并抛出异常,此为默认的拒绝策略。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
// 丢弃并吞掉异常,一般不建议吞掉异常,除非你确定这个异常不需要抛出
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
// 使用执行该拒绝策略的线程进行任务的执行,前提是线程池没有被关闭
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
// 线程池未关闭时,丢弃队列的头节点任务后继续使用该线程池执行该任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {//执行器关闭,直接丢弃任务,否则丢弃等待队列中的头节点,等待时间最长的节点,然后执行该任务
e.getQueue().poll();
e.execute(r);
}
}
}
使用线程池时注意哪些问题
最早在阿里 Java 编码规范见不建议使用 Executors 创建线程池,这里分析为什么?
这里主要集中分析 FixedThreadPool、SingleThread、CachedThreadPool、ScheduledThreadPool 这几个创建线程池的参数。
// FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* 链表本身是没有容量大小的,不像数组结构需要连续的内存空间,链表本身的容量限制于其描述他元素数量和容量边界的值
* FixedThreadPool 创建了容量边界为 Integer.MAX_VALUE 的阻塞队列,会导致该队列消耗巨大的资源,无法触发拒绝策略。
* 该阻塞队列将不停的放入任务会消耗巨大的资源,直至OOM
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
// SingleThread 与 FixedThreadPool 问题一样
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// 核心线程数为 0 ,最大线程数为 Integer.MAX_VALUE,线程 60 秒闲置将会被回收。
// 假设一个线程消耗 100k 那么需要以2的32方 (4294967296 * 100) / 1024 / 1024 / 1024 = 400T 的内存大小才能装下这么多线程,否则就会内存溢出
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 同上分析,一个线程池中,线程数量资源和队列消耗的资源需要特别注意,如果能确保执行的任务数量和线程数量在正常范围内,则是安全的
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
这几个线程池底层都是 ThreadPoolExecutor ,只不过通过线程数量和 BlockingQueue workQueue 构建出不同效果的线程池。
总结,一个线程池中,线程数量资源和队列消耗的资源需要特别注意,如果能确保执行的任务数量和线程数量在正常范围内,则是安全的。
线程池的工作流程是怎么样的?
// 从添加核心线程开始
private boolean addWorker(Runnable firstTask, boolean core) {
// ....
try {
w = new Worker(firstTask); // 创建一个工作
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 启动线程
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
// t.start() 实际执行了 Worker 中的 run() 方法
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;//已完成任务的数量
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker 在runWorker之前禁止中断
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); // 将当前的 worker 放到新创建的 Thread 中去
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
}
// runWorker() 方法,getTask() 方法是从队列中获取任务,如果获取的任务为 null 则会导致线程进入退出操作
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;//清空任务
w.unlock(); // allow interrupts 允许中断,刚创建是 -1 设置为 0;
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {//如果返回的task为null,就会导致线程被销毁
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
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 {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
// 从队列中获取去执行的任务(Runnable)
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 如果不允许
if ((wc > maximumPoolSize || (timed && timedOut))//线程数大于最大线程数或者((允许销毁核心线程数或线程数大于核心线程数)且获取任务超时)
&& (wc > 1 || workQueue.isEmpty())) {//且(线程数量大于0或者任务队列为空)
if (compareAndDecrementWorkerCount(c))
return null;//减少一个工作线程
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // 工作线程超过保活时间未获取到值则返回null
workQueue.take();//阻塞工作线程获取任务
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
什么时候关闭空闲线程?
// 1.打破线程中的轮询既 getTask() 返回值为null即可,线程自己运行完结束。线程释放前执行 finally 中定义的方法如下。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks; // 将 worker 中完成的任务数量加到 completedTaskCount 中
workers.remove(w); // 将当前线程从 workers 集合中去除
} finally {
mainLock.unlock();
}
tryTerminate(); //
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false); // 如果线程数量大于等于最小值则不创建线程,否则创建一个新的非核心线程
}
}
// 2.从1的操作中,最终还会尝试触发释放闲置线程,此时 onlyOne 为false
private void interruptIdleWorkers(boolean onlyOne) {//释放闲置的线程,
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {//线程未被中断且未执行任务,则进行释放
try {
t.interrupt();//通过中断来释放线程
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
newScheduledThreadPool 线程池的时间调度咋处理的?
// 存放任务的队列使用小顶堆结构,以执行任务的 compareTo() 方法进行排序
static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];//取出队列中第一个任务
if (first == null)
available.await();//队列中没有任务,则阻塞到可用条件等待队列中
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);//已过触发时间
first = null; // don't retain ref while waiting
if (leader != null)//有没有其他线程正在获取任务
available.await();//如果有该线程直接阻塞
else {//没有则将当前线程设置为leader,
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);//阻塞一段时间
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
}
// 任务执行完成后判断是否再放到执行队列中
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
/** Sequence number to break ties FIFO */
private final long sequenceNumber;
/** The time the task is enabled to execute in nanoTime units */
private long time;
private final long period;
public void run() {
boolean periodic = isPeriodic(); // 是否是周期性的
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime(); // 重新设置 time
reExecutePeriodic(outerTask);
}
}
}