java.util.concurrent包系列文章
JUC—ThreadLocal源码解析(JDK13)
JUC—ThreadPoolExecutor线程池源码解析(JDK13)
JUC—各种锁(JDK13)
JUC—原子类Atomic*.java源码解析(JDK13)
JUC—CAS源码解析(JDK13)
JUC—ConcurrentHashMap源码解析(JDK13)
JUC—CopyOnWriteArrayList源码解析(JDK13)
JUC—并发队列源码解析(JDK13)
JUC—多线程下控制并发流程(JDK13)
JUC—AbstractQueuedSynchronizer解析(JDK13)
线程池的组成
- 线程池管理器 -> thread-pool
- 工作线程(线程池中在运行的线程)-> t0,t1,t2…t9
- 任务队列-> blocking-queue
- 任务接口(task)-> 队列中的task1,task2…
- Executor 顶层接口,只有一个 execute方法
void execute(Runnable command);
- ExecutorService 定义了一些管理线程池,判断线程池状态的方法
void shutdown();优雅的关闭线程池(开始拒绝任务,并等待正在执行的线程执行完)
List<Runnable> shutdownNow();立即停止线程池,中断正在执行的任务
boolean isShutdown();返回线程池是否停止
<T> Future<T> submit(Runnable task, T result);提交新的任务到线程池
。。。
- ThreadPoolExecutor 真正的线程池类,Executors 的快捷创建线程池的方法内部就是new ThreadPoolExecutor()创建的线程池。
比如 newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- Executors 就是一个线程池工具类,提供了一些简单创建线程池的方法。一般不用,它的缺点后面会介绍。
ThreadPoolExecutor源码解读
void execute(Runnable command)
public void execute(Runnable command) {
// null就直接抛异常
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 首先检查是否小于核心线程数
if (workerCountOf(c) < corePoolSize) {
// 如果当前工作的线程数小于核心线程数,则增加一个线程来运行任务,command就是提交进来的任务
// 第二个参数的意思:true表示在增加线程时判断是否小于核心线程数,false表示判断是否小于最大线程数
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);
// 如果发现当前运行的线程为0了,则直接创建新线程执行任务
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 走到这里说明,要么线程数大于等于核心线程,要么线程池停止了,要么队列也满了
// 就直接创建新的线程执行任务(当然,这个数量要小于最大线程数,如果达到了最大线程数,则执行拒绝策略)
// addWorker()方法返回是否增加成功
else if (!addWorker(command, false))
reject(command);
}
以上就是线程池最核心的流程,后面会给出流程图说明。
再来看看线程池是如何做到线程不停止,直接复用执行任务的。
boolean addWorker(Runnable firstTask, boolean core);
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 这一部分是for死循环,通过CAS操作修改当前线程数
for (int c = ctl.get();;) {
// Check if queue empty only if necessary.
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false;
for (;;) {
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateAtLeast(c, SHUTDOWN))
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 最重要的就是这里,把提交的任务封装成了Worker对象
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
// 加锁,把任务添加到队列中
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
if (t.getState() != Thread.State.NEW)
throw new IllegalThreadStateException();
// workers是一个HashSet<Worker> ,接下来看runWorker()方法怎么复用执行任务的
workers.add(w);
workerAdded = true;
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
void runWorker(Worker w)
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 复用的原理:while循环,通过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);
try {
// 最关键的这里,就是执行了我们提交的每一个线程任务的run方法来执行任务的。只不过Worker 包装了一下
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
最后看看
Runnable getTask()
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
// Check if queue empty only if necessary.
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, 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;
}
}
}
源码就贴到这里,线程池的整个流程应该比较清晰了。下面再补充一下。
ThreadPoolExecutor参数
- corePoolSize:核心线程数,线程池完成初始化以后,线程池中并没有任何线程,线程池会等待有任务到来时,再创建线程去执行任务。
- maxPoolSize:最大线程数
- keepAliveTime:空闲线程存活时间。如果线程池当前的线程数多于corePoolSize,多于的线程空闲时间超过keepAliveTime,它们就会终止
- ThreadFactory:新的线程由ThreadFactory创建,默认使用Executors.defaultThreadFactory(),自己制定ThreadFactory,可以设置线程名,线程组,优先级,是否是守护线程等。
- workQueue:工作队列
- 直接交接:SynchronousQueue,不存储任务,任务进来直接创建线程处理,可以无限创建线程
- 无界队列:LinkedBlockingQueue,如果处理的速度跟不上任务提交的速度,那么任务会越来越多。有OOM风险。
- 有界队列:ArrayBlockingQueue,可以设置队列大小
添加线程的规则流程
新任务来的时候,即使其他工作线程处于空闲,也会创建一个新线程来运行新任务。 如果线程数>=corePoolSize但少于maxPoolSize。则将任务放入队列中。如果队列已,并且线程数小于maxPoolSize,则创建一个新线程来运行任务。如果队列已满并且线程数>=maxPoolSize,则拒绝该任务。
JDK默认提供的线程池(Executors工具类中)
- newFixedThredPool:无界队列,容易造成占用大量内存导致OOM。
- newSingleThreadExecutor:单线程,无界队列,请求堆积的时候也会导致OOM。
- newCachedThreadPool:corePoolSize设置为0,maxPoolSize设置为Integer.MAX_VALUE使用的SynchronousQueue。来了任务直接就给线程执行了。请求堆积的时候也会导致OOM。用完线程就全部回收销毁。
- newScheduledThreadPool:延迟队列DelayedWorkQueue,支持定时以及周期性执行任务。使用于要重复运行的任务。类似于定时任务。
- workStealingPool:1.8加入。处理子任务。子任务放到每个线程独有的任务队列中,某个线程执行完了,可以窃取别的线程中子任务来执行。递归+子任务的场景使用
线程池的状态
- RUNING:接受新任务并处理排队任务
- SHUTDOWN:不接受新任务,但处理排队任务
- STOP:不接受新任务,也不处理排队任务,并中断正在执行的任务
- TIDING:所有任务都已终止,workCount为零时,线程会转换到TIDING状态。并将运行terminate()钩子方法。
- TERMINATED:terminate()运行完成
停止线程池
- shutdown:优雅的关闭线程池。执行完队列中的存量任务,拒绝新任务加入。
- isShutdown:线程是否进入停止状态(boolean)。
- isTerminated:返回线程是否真的停止,指任务全都执行完毕。
- awaitTermination:检测线程是否在指定的时间内停止了。会阻塞等待指定时间。
- shutdownNow:立刻关闭线程池 。正在执行的线程会收到interruptedException异常。返回一个没有执行的任务List。
线程池拒绝任务
- 当Executor关闭时,提交新任务会被拒绝。
- 当Executor线程满和队列也满了会拒绝。
拒绝策略
- AbortPolicy:抛出异常
- DiscardPolicy:丢弃新提交的任务
- DiscardOldestPolicy:丢弃最老的任务
- CallerRunsPolicy:谁提交的任务交给谁去执行(让提交任务的线程去执行)
线程池的线程设置为多少比较合适
- CPU密集型(加密、计算等):最佳线程数为CPU核心的1-2倍
- 耗时IO型(读写数据库、文件、网络读写):最佳线程数为CPU核心的5-10倍
- 线程数=CPU核心数*(1+平均等待时间/平均工作时间)