线程池简介
使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
jdk1.5后新增Executors和ThreadPoolExecutor类,是Java提供的用于管理线程池的类。
三种常见的线程池详解
创建线程池
//可缓存线程池
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//可重用固定个数的线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
//单线程化的线程池
ExecutorService newSingleThreadPool = Executors.newSingleThreadExecutor();
查看底层调用
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
可以看出,这3中线程池底层最终都调用了相同的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
首先我们弄清楚这些参数的含义:
int corePoolSize:核心线程数
int maximumPoolSize:最大线程数
long keepAliveTime:空闲线程存活时间
TimeUnit unit:存活时间单位
BlockingQueue workQueue:工作队列
ThreadFactory threadFactory:创建一个新线程时使用的工厂
RejectedExecutionHandler handler:拒绝策略
newCachedThreadPool意思构造一个最大线程数无限大的线程池,有新任务过来无空闲线程则新增线程。
newFixedThreadPool意思构造一个核心线程数和最大线程数相同、工作队列无限大的线程池,有新任务来先交给核心线程,核心线程都繁忙则全部放入队列。
newSingleThreadExecutor意思构造一个核心线程数和最大线程数都为1、工作队列无限大的线程池,有新任务来一般都放入队列中,线程一个个执行任务。
从这些参数可以看出,这3种Executors类提供的线程池都存在风险。阿里巴巴java开发手册中明确规定,不允许使用Executors类生成线程池,而是按照业务需要使用ThreadPoolExecutor类自定义参数生成线程池。
为什么呢?首先看newCachedThreadPool创建的线程,任务量大时会无限创建线程,可能CPU消耗很大或JVM最大线程数异常到时OOM。newFixedThreadPool和newSingleThreadExecutor创建的线程,任务量大时会将全部任务放入队列,导致OOM。
线程池提交、执行任务顺序
提交任务顺序
执行任务顺序
线程池源码
线程池提交任务分为submit和execute方法,submit中调用了execute方法,直接上execute代码。
public void execute(Runnable command) {
//判断任务为空扔空指针异常
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.判断工作线程数小于核心线程数,直接addWorker
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.判断线程状态为running且放入工作队列成功
//workQueue.offer()成功返回true
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//再次检验线程池状态,不为running则从工作队列移除,执行拒绝策略
//可能期间执行shutdownNow操作,故需要再次检验线程池状态
if (! isRunning(recheck) && remove(command))
reject(command);
//判断无线程工作,则执行队列的线程任务
//注意此处addWorker的Runnable为null,因为已放入阻塞队列中
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3.最后尝试加入到工作线程,不成功则执行拒绝策略
//此处添加到非核心线程的工作线程(最大线程数量-核心线程数量)
else if (!addWorker(command, false))
reject(command);
}
提交任务逻辑:
1、提交任务先交给核心线程处理
2、其次放入工作队列
3、然后交给非核心线程处理
4、最后如果非核心线程也满,则执行拒接策略(默认抛异常)
继续上addWorker源码
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//校验线程状态和工作队列为空
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
//自旋+CAS增加工作线程数
for (;;) {
int wc = workerCountOf(c);
//判断工作线程有无超出
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS增加工作线程数,成功跳出自旋
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
//线程状态错误继续自旋
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//加可重入锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//加入workers,成功执行t.start()
//t为Worker的thread属性,t.start()则启动线程,执行run()
//public void run() {runWorker(this);}将执行runWorker()
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
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;
}
最后扒一扒runWorker源码,就大功告成
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
//此处是重点
//1.判断task不为空则执行task,意味先执行核心和非核心线程池中的task
//2.两者都没有task ,则取出工作队列中task执行
while (task != null || (task = getTask()) != null) {
w.lock();
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);
}
}
执行任务逻辑:
1、先执行核心线程和非核心线程中的任务
2、其次执行工作队列的任务
线程池超时回收
上文中说道runWorker方法中,获取task的方式有两种,w.firstTask或者从阻塞队列中获取。下面具体说说getTask方法。
private Runnable getTask() {
boolean timedOut = false;
//自旋获取
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//1、检查线程池状态为SHUTDOWN
//2、且状态为STOP或阻塞队列为空,直接返回
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
//关键代码
//allowCoreThreadTimeOut为核心线程为空是否可以回收标识
//默认为false,表示不回收核心线程,可调用allowCoreThreadTimeOut()修改
//当核心线程可回收或者工作线程大于核心线程数,表示可回收线程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//特殊情况检查
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//根据上面判断的timed变量,调用不同方法
//true调用poll 队列有值移除返回值,无值则阻塞线程
//若指定时间则超过时间返回null
//false调用take 队列有值移除返回值,无值则阻塞线程直到有值
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
总结下,allowCoreThreadTimeOut不做修改时都为false,则只有工作线程大于核心线程数时,若在超时时间内未取到值则方法返回null,会跳出runWorker方法中while循环,执行finally中的processWorkerExit方法,大体意思为workers.remove(w),移除work。
到此线程池的工作核心流程包括创建、执行、超时销毁都一清二楚了,多谢大家观看。