为什么要用线程池?
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用
- 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因线程过多消耗内存,也避免了因线程过少,浪费系统资源
如何做到每个工作线程都可以被重复利用呢?先看下线程池的工作原理
原理如上图,线程池有七个核心参数
- corePoolSize 线程池核心线程数
- maximumPoolSize 线程池最大线程数量
- keepAliveTime 空闲线程存活时间
- unit 空闲线程存活时间单位
- workQueue 工作队列
- threadFactory 线程工厂
- handler 拒绝策略
线程池之所以能做到重复利用,是因为线程池的核心线程不会被摧毁,执行完任务后会重复利用
那么线程池是如何保持核心线程不被摧毁呢?
首先看先线程池是如何处理任务的,如下图
下面我们看下核心部分源码:
- 当有一个任务添加进来时,线程池会创建一个Worker,Worker是实现Runnable方法的,所以Worker执行时会调用run方法,run方法会接着调用runWoker方法
- 主要看这一行代码,调用getTask方法获取任务并且执行 while (task != null || (task = getTask()) != null)
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 主要看这一行代码,调用getTask方法获取任务并且执行
while (task != null || (task = getTask()) != 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);
}
}
- 看下getTask方法是如何实现的
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())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 主要看这几行代码
- getTask方法通过调用任务队列的take方法,不断的获取线程
- 如果任务队列里面数量为0,则会一直阻塞,一直等到有任务加入,从而保证了核心线程不被摧毁