Executor框架
一、简介
什么是Executor框架呢?
Executor框架说白了就是一个线程池,用于存放控制线程的启动、执行、关闭。
Executor是一个灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
每次创建线程new Thread()都比较消耗性能、消耗资源。并且这种方式创建线程缺乏管理。
Executor框架包括:各种线程池、Executor、Executors、ExecutorService,CompletionService、Future、Callable等。
Executor:执行器接口,定义runnable任务执行的方式。
ExecutorService:执行器服务接口,定义对Executor的服务。
ScheduledExecutorService:时间调度执行器服务接口,定义时间调度。
AbstractExecutorService:执行器服务抽象类,实现了接口的默认方法。
ThreadPoolExecutor:线程池具体实现。
Executors:线程池的工厂类,创建各种线程池。
二、ThreadPoolExecutor
在Executors中的各种线程池,就是用ThreadPoolExecutor的方式创建的。
常量及属性
线程池之ThreadPoolExecutor状态控制
后29位存放当前线程数,前3位存放运行状态,总共有5种状态,只能用3位以上来表示。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//记录运行状态和当前线程数量
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//线程的最大数量
//运行状态:可以接受新任务,并执行已经加入任务队列的任务
private static final int RUNNING = -1 << COUNT_BITS;
//关闭状态:不接受新任务,但仍然执行已经加入任务队列的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//停止状态:不接受新任务,不执行已经加入任务队列的任务,还中断正在执行的任务
private static final int STOP = 1 << COUNT_BITS;
//调整回收状态:所有任务被停止,清空所有线程(workerCount=0)进入该状态,然后执行terminated()方法
private static final int TIDYING = 2 << COUNT_BITS;
//终止状态:terminated()方法执行完成后进入该状态
private static final int TERMINATED = 3 << COUNT_BITS;
//获取线程池运行状态(即取高三位)
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
数据结构
Worker,实现了runnable接口,继承了AQS
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable {
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;//指向运行该任务的线程
/** Initial task to run. Possibly null. */
Runnable firstTask;//我们需要运行的任务,也就是从线程池中传入的。
/** Per-thread task counter */
volatile long completedTasks;
...
}
在ThreadPoolExecutor中用HashSet将Worker封装起来。
构造方法:
构造器参数:
int corePoolSize:核心线程数,如果当前线程数<核心线程数,就会创建一个新线程执行任务,即使线程池中存在空闲线程。如果当前线程数>核心线程数,就会将新任务放入workQueue中,等待线程池任务调度。
int maximumPoolSize:最大线程数,如果workQueue已满,且当前线程数<maximumPoolSize,会创建新的线程执行任务放入线程池中,直到当前线程数=maximumPoolSize。如果是无界的任务队列,这个参数无效。
int keepAliveTime:线程存活的时间。如果当前线程数>核心线程数,超过这个存活时间之后就会销毁。
TimeUtil unit:keepAliveTime的时间单位。
BlockingQueue<Runnable> workQueue:任务的阻塞队列。用于存放任务。
ThreadFactory:用于创建线程的工厂,可以通过工厂设置线程的name、debug和定位时间等。
RejectedExecutionHandler:拒绝策略,如果当前线程数>最大线程数,根据拒绝策略进行处置。
- AbortPolicy:丢弃任务并抛出RejectedExecutionHandler异常。默认。
- DiscardPolicy:丢弃任务但是不抛出异常。
- DiscardOldestPolicy:丢弃队列中最前面的任务,然后尝试重新执行。
- CallerRunsPolicy:由调用线程处理该任务。
- 自定义
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;
}
执行
ThreadPoolExecutor源码分析
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();//获取当前线程数和执行状态
if (workerCountOf(c) < corePoolSize) {//获取当前线程数与corePoolSize作比较
if (addWorker(command, true))//尝试创建核心线程放入线程池,成功则返回
return;
//返回失败,可能是因为当前线程数已经达到corePoolSize,或者由于线程池状态
c = ctl.get();//更新执行状态
}
if (isRunning(c) && workQueue.offer(command)) {//如果执行状态是Running,就将任务加入队列
int recheck = ctl.get();//更新执行状态
if (! isRunning(recheck) && remove(command))//如果执行状态不是Running,就删除任务并调用拒绝策略
reject(command);
else if (workerCountOf(recheck) == 0)//如果当前运行线程数为0
addWorker(null, false);//创建非核心线程
}
else if (!addWorker(command, false))//尝试向线程池中添加一个非核心线程执行command
reject(command);//运行状态原因或队列满了,失败则调用拒绝策略
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);//获取运行状态
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&//非运行状态
! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
//这个条件可替换为(rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
//也就是说 执行状态为SHUTDOWN 且 执行任务为null 且 队列非空的时候 返回false。所以SHUTDOWN,不接受新任务,但是会执行队列中已有的任务。
return false;
for (;;) {
int wc = workerCountOf(c);//获取当前线程数
if (wc >= CAPACITY ||//如果线程数大于等于最大容量2^29-1 在这里控制线程数大小
wc >= (core ? corePoolSize : maximumPoolSize))
//core为false表示创建非核心线程,wc>=maximumPoolSize,不会创建非核心线程
//core为true表示创建核心线程,wc>=corePoolSize,不会创建核心线程
return false;
if (compareAndIncrementWorkerCount(c))//用cas方法将线程数+1
break retry;//成功后跳出最外层循环
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)//如果运行状态发生了改变,继续进入外层循环
continue retry;
//否则继续内层循环,重试尝试cas,这里用了自旋锁
// else CAS failed due to workerCount change; retry inner loop
}
}
//上面添加线程数量成功,开始添加线程
boolean workerStarted = false;//线程启动标志位
boolean workerAdded = false;//线程是否加入workers标志位
Worker w = null;
try {
w = new Worker(firstTask);//新建一个worker
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 获取锁之后重新检查运行状态,可能在上一个获取到锁处理的线程改变了执行状态
// 比如线程工厂创建失败,线程池被shutdown
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||//如果执行状态是running
//或者执行状态是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();//启动线程,执行worker里面的thread的run方法
workerStarted = true;//设置启动标志位
}
}
} finally {
if (! workerStarted)//如果没有启动成功
addWorkerFailed(w);//说明添加线程失败
}
return workerStarted;
}
//worker类的构造器以及run
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//Worker类实现了Runnable
//这里创建的thread执行的任务是worker自己
}
public void run() {
runWorker(this);
}
//线程池的线程复用
//将队列中的任务不断取出,调用run方法进行执行。而.start()只调用一次
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();//当前运行的线程就是调用线程
Runnable task = w.firstTask;//获取worker的任务
w.firstTask = null;
w.unlock(); // 允许被interrupt
boolean completedAbruptly = true;
try {
//循环知道task==null(线程池关闭、超时等) getTask()从阻塞队列中获取任务
while (task != null || (task = getTask()) != null) {
w.lock();//执行任务前上锁
//stop以上状态:不接受新任务,不执行队列中的任务,中断执行中的任务
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;//将task置空,执行下一个任务
w.completedTasks++;//完成的任务数加1
w.unlock();
}
}
completedAbruptly = false;//说明不是用户异常引起
} finally {
processWorkerExit(w, completedAbruptly);
}
}
//从阻塞队列中获取任务
private Runnable getTask() {
boolean timedOut = false; //取任务是否超时
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 如果执行状态大于等于SHUTDOWN状态,并且阻塞队列为空 或者 如果执行状态大于STOP状态
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();//工作线程数减一,因为在启动线程之前已经加一了
return null;//线程将会退出
}
int wc = workerCountOf(c);
// allowCoreThreadTimeOut 是否允许核心线程超时
// wc>corePoolSize
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 如果工作线程数大于最大线程数,或者 允许超时并且确实超时了
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {//并且工作线程数大于1 或者 阻塞队列为空
if (compareAndDecrementWorkerCount(c))//cas进行减一
return null;//线程将会退出
continue;//上面线程数减一失败,说明线程数量已被抢先改变,继续循环,
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
//移除并返回队首元素
if (r != null)
return r;
timedOut = true;//如果取出为null,说明取任务超时
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
执行监控
我们需要覆盖这三种方法做具体实现。
protected void beforeExecute(Thread t, Runnable r) { }//执行之前
protected void afterExecute(Runnable r, Throwable t) { }//执行之后
protected void terminated() { }//退出之前
线程池的关闭
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();//对线程检查是否有权限修改
advanceRunState(SHUTDOWN);//改变线程池状态位SHUTDOWN
interruptIdleWorkers();//中断所有线程
onShutdown(); //留给子类实现,在shutdown做的一些事情 比如ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
shutdown() 、 shutdownNow() 、 awaitTermination() 的用法和区别
各种线程池
2.1 newFixedThreadPool
每次提交一个任务就创建一个线程放入线程池,直到线程达到最大线程数。然后会保持不变。如果某个线程因为执行异常而结束,就会线程池就会补充一个新线程。
创建固定大小的线程池,如果任务数目大于最大线程数,那么没有执行的任务必须等到有任务完成为止。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2.2 newSingleThreadExecutor
只有一个线程,相当于一个线程串行执行所有任务。如果该线程出现异常,就会有新的线程来代替。执行顺序即提交顺序。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
2.3 newCachedThreadPool
可缓存的线程池
如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲线程(60s内不执行任务),当任务数增加时,此线程池又可以智能的添加新线程来执行任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
自动扩展线程池的大小,核心线程数为0,意味着每次都是向workQueue中放。
等学了JUC中的集合再来补
SynchronousQueue
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
2.4 newScheduledThreadPool
定时任务调度的线程池。
创建一个大小无限的线程池。定时或周期性执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
2.5 newSingleThreadScheduledExecutor
单线程定时任务调度
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
三、线程池不要用Executors创建
在阿里巴巴开发手册中指出:
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors各个方法的弊端:
- FixedThreadPool和SingleThreadExecutor
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
这是因为使用LinkedBlockingQueue的无惨构造器使得阻塞队列的容量为Integer.MAX_VALUE - CachedThreadPool 和 ScheduledThreadPool
主要问题是线程数最多数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
maximumPoolSize为Integer.MAX_VALUE
线程池创建的方式:
- 引入commons-lang3包
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
- 引入com.google.guava包
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
pool.execute(()-> System.out.println(Thread.currentThread().getName()));
pool.shutdown();//gracefully shutdown
- spring配置线程池方式:自定义线程工厂bean需要实现ThreadFactory,可参考该接口的其它默认实现类,使用方式直接注入bean
<bean id="userThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="queueCapacity" value="2000" />
<property name="threadFactory" value=threadFactory>
<property name="rejectedExecutionHandler">
<ref local="rejectedExecutionHandler" />
</property>
</bean>
ThreadPoolExecutor:线程池不允许使用Executors创建
四、AbstractExecutorService
AbstractExecutorService是一个抽象类实现了ExecutorService接口。我们可以在看一下ThreadPoolExecutor,继承了这个抽象类。
能力不高水平有限,在文章中大部分借鉴了其他博客的内容,以及很多未能解决的问题,请大家多多包涵。侵删