1.线程池执行过程
这里贴出
ThreadPoolExecutor(Runnble command)源码,源码解析参考了这个博客点我和自己的理解:
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
* 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.
*
* 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.
*/
// 获取当前线程池的状态以及线程池中线程数量,高3位表示线程池状态,后面的位数表示线程个数
int c = ctl.get();
// 如果线程池的线程数量 < 核心线程数
if (workerCountOf(c) < corePoolSize) {
// 添加一个核心线程执行任务,true 表示为核心线程
if (addWorker(command, true))
return;
// 再次获取线程池状态和线程池中线程数量,防止并发的执行execute方法导致的线程池发生变化
c = ctl.get();
}
// 如果if语句要返回ture,需要满足两个条件
// 1.线程池处于RUNNING状态(只有处于此状态,才能接受新的任务)
// 2.添加到任务队列成功
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果队列也满了,就创建一个非核心线程(core==false)执行任务
else if (!addWorker(command, false))
// 创建非核心线程执行任务失败,执行拒绝策略
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 双层死循环的第一层循环
for (;;) {
int c = ctl.get();
// 获取当前线程池状态
int rs = runStateOf(c);
// 这一行代码看起来有点难以理解,返回 false 表示无法接收或处理任务
// 如果不想返回 false,也就是 if 语句判断不成立,需要满足以下两个条件之一:
// 1. rs < SHUTDOWN, 也就是 rs == RUNNING(我们之前讲过 RUNNING 表示可以接受新的任务)
// 2. rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()
// 2.1 rs == SHUTDOWN 表示不会接收新的任务,但是会执行阻塞队列中的任务
// 2.2 firstTask == nul 表示要执行阻塞队列中的任务
// 2.3 阻塞队列不能为空
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 双层死循环的第二层循环
for (;;) {
// 获取当前工作线程的数量
int wc = workerCountOf(c);
// 返回false符合下面条件之一
// 1.当前工作线程的数量 > 最大容量(CAPACITY)
// 2.如果core=true,则当前工作线程的数 > 核心线程数时,返回false;
// 如果core=fasle,则当前工作线程数 > 最大线程数,返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 通过CAS(自旋)操作, 把线程池中的线程数量+1,然后跳出双层循环,进入第二部分
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// CAS操作失败,就核对线程池的状态(因为ctl是共享变量,同时执行addWorker方法时,CAS不一定成功)
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// 第二部分,走到这里表示线程池可以创建新的线程,或者可以执行队列中的任务
// 两个标记,见名知义
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 包装需要执行任务(注意 firstTask 可能为 null)
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 rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 添加至 workers 集合
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
// 添加成功
workerAdded = true;
}
} finally {
// 释放锁
mainLock.unlock();
}
if (workerAdded) {
// 启动线程
t.start();
// 启动成功
workerStarted = true;
}
}
} finally {
// 如果线程启动失败, 就删除 workers 集合中刚添加的 Worker 对象
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
2.线程池创建的几种方式
2.1使用Executors线程池工具类(相当于工厂类)创建线程池
a)Executors.newCachedThreadPool();
创建可缓存的线程池
b)Executors.newFixedThreadPool();
创建固定数目的线程池
c)Executors.newSingleThreadPoolExecutor();
创建单线程化线程池
d)Executors.newScheduledThreadPool();
创建一个支持定时及周期性执行任务的线程池
2.2手动创建线程池
通过构造函数创建线程池
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
各个参数的含义:
- corePoolSize:核心线程池大小;
- maximumPoolSize:最大线程数;
- keepAliveTime:超出核心线程数量的其余线程,如果空闲时间超过keepAliveTime,就会被退出(比如核心线程数10,最大线程数30,当线程池中线程数达到30时,可能新创建的这20个线程相当于是“借”的,如果这20个线程空闲时间超过keepAliveTime,就会被退出);
- unit:keepAliveTime的时间单位;
- workQueue:阻塞队列,会在其它系列文章会详细讲解;
- threadFactory:创建线程的工程类。如果项目中有多个线程池,可以通过指定线程工厂为每个创建出来的线程设置更有意义的名字。如果出现并发问题,也方便查找问题原因;
- handler:饱和策略。当线程池的阻塞队列已满和指定的线程都已经开启,说明当前线程池已经处于饱和状态了,那么就需要采用一种策略来处理这种情况。采用的策略有这几种:
- AbortPolicy: 直接拒绝所提交的任务,并抛出RejectedExecutionException异常;
- CallerRunsPolicy:只用调用者所在的线程来执行任务 具体理解请参考
- DiscardPolicy:不处理直接丢弃掉任务;
- DiscardOldestPolicy:丢弃掉阻塞队列中存放时间最久的任务,执行当前任务
例如:
ExecutorService threadPool = new ThreadPoolExecutor(2,5,
1L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
提供一个手动创建线程池的工具类
- 导入guava的jar包
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> - 工具类ThreadPoolUtil
public class ThreadPoolUtil { public static ExecutorService createThreadPool(String threadPoolName, int corePoolSize, int maximumPoolSize, int queueSize) { ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(threadPoolName + "-%d").build(); ExecutorService pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(queueSize), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); return pool; } }
3.Spring框架对线程池的管理
Spring中ThreadPoolTaskExecutor类对ThreadPoolExecutor类进行了封装,通过注册ThreadPoolTaskExecutor的bean来实现线程池的创建
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程池数量 -->
<property name="corePoolSize" value="2"></property>
<!-- 允许空闲时间,注意时间的单位是秒 -->
<property name="keepAliveSeconds" value="200"></property>
<!-- 线程池维护线程的最大数量 -->
<property name="maxPoolSize" value="5"></property>
<!-- 缓存队列的大小 -->
<!-- queueCapacity=0,则创建SynchronousQueue缓存队列 -->
<!-- queueCapacity>0,则创建LinkedBlockingQueue缓存队列 -->
<property name="queueCapacity" value="1"></property>
<!-- 对拒绝task的处理策略 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
return (BlockingQueue)(queueCapacity > 0 ? new LinkedBlockingQueue(queueCapacity) : new SynchronousQueue());
}
4.SpringBoot对线程池的管理
@Configuration
public class AsyncConfiguration {
@Bean("test1-executor")
public ThreadPoolExecutor asyncExecutor() {
int cpu = Runtime.getRuntime().availableProcessors();
// 根据需求设置缓存队列的大小
return new ThreadPoolExecutor(cpu, cpu << 2, 0L, TimeUnit.MILLISECONDS, new LinkedTransferQueue<>());
}
@Bean("test2-executor")
public ThreadPoolExecutor redisExecutor() {
// 根据需求设置缓存队列的大小
return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedTransferQueue<>());
}
}
注入时参考如下:
@Component
public class Test1 {
@Autowired
@Qualifier("test1-executor")
ThreadPoolExecutor threadPool;
}
@Component
public class Test2 {
@Autowired
@Qualifier("test2-executor")
ThreadPoolExecutor threadPool;
}
5.为什么不推荐通过Executors直接创建线程池
根据上面的总结,我们可以发现,使用Executors的方法创建线程池是比较简单,那么为什么阿里巴巴java开发手册中,明确指出,不允许使用Executors创建线程池。
4. 【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1) FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2) CachedThreadPool: 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
分析如下:
newFixedThreadPool和newSingleThreadExecutor创建线程池时使用的阻塞队列是LinkedBlockingQueue,并且未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出的问题。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory)
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
newCachedThreadPool允许创建的非核心线程数量是Integer.MAX_VALUE,可能会导致创建大量的线程,从而导致OOM
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}