线程池(Thread Pool)
涉及线程池相关类及接口(jdk1.5 juc开始):Executor、Executors(工具类)、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等
JDK中默认使用的线程池: ThreadPoolExecutor
一、理论:
1.1、简介:
-
多线程处理形式
-
处理过程:
- 任务添加队列
- 创建线程时自动启动任务
- 线程池的线程均是后台线程,每个线程均使用默认的堆栈大小、以默认的优先级使用,并处于多线程单元中
- 如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙
- 如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。
- 超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
-
池化技术(数据库连接池(jdbc)、HttpClient连接池)
-
主要作用:减少频繁创建线程、销毁线程、线程上下文切换问题
-
模式:HS/HA半同步/半异步模式、L/F领导者与跟随者模式。
- HS/HA半同步/半异步模式:生产者消费者模式
- 分为同步层、队列层、异步层三层。
- 同步层的主线程处理工作任务并存入工作队列,工作线程从工作队列取出任务进行处理,
- 如果工作队列为空,则取不到任务的工作线程进入挂起状态。
- 由于线程间有数据通信,因此不适于大数据量交换的场合。
- L/F领导者跟随者模式:在线程池中的线程可处在3种状态之一:
- 领导者leader、追随者follower或工作者processor
- 任何时刻线程池只有一个领导者线程。
- 事件到达时,领导者线程负责消息分离,并从处于追随者线程中选出一个来当继任领导者,然后将自身设置为工作者状态去处置该事件。处理完毕后工作者线程将自身的状态置为追随者。
- 整个过程中Leader的状态变化:事件到达时,追随中遴选领导、自身设置为工作者、处理完毕设置为追随者
- 模式实现复杂,避免线程间交换任务数据,提高了CPU CACHE相似性。在ACE(Adaptive Communication Environment)中,提供了领导者跟随者模式实现。
- HS/HA半同步/半异步模式:生产者消费者模式
1.2、线程池创建、销毁及核心参数:
核心参数:
最全的构造方法:7参
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:(int)核心线程数,线程池中常驻线程数、线程池初始化默认没有线程、当任务来临时才创建线程去执行任务
-
maximumPoolSize:(int)最大线程数,核心+非核心,
- 只有在workQueue满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
-
keepAliveTime:(long)非核心线程的空闲时间,超过keepAliveTime自动终止并回收,特殊情况,corePoolSize=maximumPoolSize时,keepAliveTime参数无效,设置allowCoreThreadTimeOut(true),则会也作用于核心线程。
- 当线程池中空闲线程数量超过corePoolSize时,多余的线程就叫非核心线程。
-
unit:keepAliveTime的时间单位(TimeUnit)
- NANOSECONDS : 1微毫秒 = 1微秒 / 1000 纳秒
- MICROSECONDS : 1微秒 = 1毫秒 / 1000
- MILLISECONDS : 1毫秒 = 1秒 /1000
- SECONDS : 秒
- MINUTES : 分
- HOURS : 小时
- DAYS : 天
-
workQueue:
-
BlockingQueue
-
等待队列,保存任务
-
分类:有界、无界、同步移交
-
池中工作线程数大于corePoolSize时,此时新进来的任务放到此队列中
-
SynchronousQueue(同步移交队列):队列不作为任务的缓冲,相当于队列长度为0,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于 阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
-
LinkedBlockingQueue(无界队列):长度不受限制,请求太多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多OOM,不指定容量,默认为
Integer.MAX_VALUE
,内部由两个ReentrantLock来实现出入队列的线程安全,由各自的Condition对象的await和signal来实现等待和唤醒功能 -
ArrayBlockintQueue(有界队列):长度受限,队列满了创建多余的线程来执行任务,由数组支持,按 FIFO(先进先出)原则对元素进行排序。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
-
PriorityBlockingQueue(无界队列):是一个按照优先级进行内部元素排序的无界阻塞队列。队列中的元素必须实现 Comparable 接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部;PriorityBlockingQueue 不会保证优先级一样的元素的排序。
-
DelayedWorkQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素
-
-
threadFactory:
-
ThreadFactory
-
创建线程的工厂
-
默认使用Executors.defaultThreadFactory()
-
可以使用guava库的ThreadFactoryBuilder来创建
-
默认工厂创建的线程:同属于相同的线程组,具有同为 Thread.NORM_PRIORITY 的优先级,以及名为 “pool-XXX-thread-” 的线程名(XXX为创建线程时顺序序号),且创建的线程都是非守护进程。
-
-
handler:
- RejectedExecutionHandler
- 线程池无法继续接收任务(队列已满且线程数达到maximumPoolSize)时饱和策略
- 取值:AbortPolicy(拒绝策略)、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
- AbortPolicy(拒绝策略) 默认,中断抛出异常
- DiscardPolicy:默默丢弃任务、不进行任何通知
- DiscardOldestPolicy:丢弃在队列中存在时间最久的任务
- CallerRunsPolicy:让提交任务的线程去执行任务
注意:
容易出问题:corePoolSize、maximumPoolSize、handler、workQueue、
- corePoolSize和maximumPoolSize设置不当会影响效率,甚至耗尽线程
- workQueue设置不当会导致OOM
- handler设置不当会导致提交任务时抛出异常
创建:
手动创建:new ThreadPoolExecutor
自动创建:Executors工具类
创建流程:
如果线程数小于coreSize,但如果有空闲线程,就取消创建线程的逻辑. 在有空闲线程的情况下,直接将任务放入队列中,即达到任务执行的目的。
- 提交任务
- 判断核心线程池是否已满,如已经满了,走第三步,否则直接创建线程来执行任务
- 判断阻塞队列是否已满,如已经满了,走第四步,否则直接将任务放入队列中
- 判断线程池是否已满(是否大于最大线程数),如已经满了,走第五步,否则继续创建线程来执行任务
- 执行拒绝策略
- AbortPolicy(拒绝策略) 默认,中断抛出异常
- DiscardPolicy:默默丢弃任务、不进行任何通知
- DiscardOldestPolicy:丢弃在队列中存在时间最久的任务
- CallerRunsPolicy:让提交任务的线程去执行任务
Executors工具类创建:
池名 | 描述 | 队列 |
---|---|---|
newFixedThreadPool | 定长线程池 | 无界队列 |
newSingleThreadExecutor | 单线程 | 无界队列 |
newCachedThreadPool | 可缓存 | 同步移交队列 |
newScheduledThreadPool | 持定时周期性执行 | 延迟队列 |
- FixedThreadPool和SingleThreadExecutor均使用无界队列,因为这两个线程池每个(核心线程和最大线程线程)个数都一样,线程数无法动态扩展,故使用无界队列来应对不可知的任务量
- CachedThreadPool则使用的是SynchronousQueue同步移交队列,因为此线程池中核心线程数为0,总线程数为最大值,来一个任务就创建一个线程来执行任务,用不到队列来存储任务
/**
jdk1.5
*/
public class Executors {
/**
* 定长线程池,可控制线程最大并发数,超出的线程在无界队列中等待
* @Param 线程个数作为最大线程数maximumPoolSize
* 最大线程数maximumPoolSize等于corePoolSize,此时非核心线程无空闲时间失效,自动终止并回收
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);
}
/**
* 单线程线程池,可控制线程最大并发数,超出的线程在无界队列中等待
* 最大线程数maximumPoolSize等于corePoolSize,此时非核心线程无空闲时间失效,自动终止并回收
* 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),threadFactory));
}
/**
* 可缓存线程池,可控制线程最大并发数,超出的线程在无界队列中等待
* 最大线程数maximumPoolSize为Integer最大值2^32 -1
* corePoolSize 为0
* 非核心线程空闲时间1分钟后自动回收
* 同步移交队列,长度为0,队列不作为任务的缓冲
* 线程池长度超过处理需要,可灵活回收空闲线程,若没有可回收的,则新建线程
* 不维护常驻线程(核心线程),每次请求直接创建线程来处理任务
* 一定情况下会导致OOM
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),
threadFactory);
}
/**
* jdk1.5 之前:Timer来实现定时周期性
* 单线程运行,一旦任务执行缓慢,下一个任务就会推迟,而如果使用了ScheduledThreadPoolExecutor线程数可以自行控制
* 当Timer中的一个任务抛出异常时,会导致其他所有任务不在执行
* ScheduledThreadPoolExecutor可执行异步的任务,从而得到执行结果
* 支持定时周期性执行,注意一下使用的是延迟队列,
* corePoolSize 具体值
* 最大线程数maximumPoolSize为Integer最大值2^32 -1
* 非核心线程空闲时间0,非核心线程无空闲时间失效,自动终止并回收
* 延迟队列
* 弊端同newCachedThreadPool一致
*
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
//实际走的
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
}
手动创建线程池:
建立自己的线程工厂类,灵活设置关键参数:
//这里默认拒绝策略为AbortPolicy
private static ExecutorService executor = new ThreadPoolExecutor(10,10,60L, TimeUnit.SECONDS,new ArrayBlockingQueue(10));
//使用guava包中的ThreadFactoryBuilder工厂类来构造线程池:
private static ThreadFactory threadFactory = new ThreadFactoryBuilder().build();
private static ExecutorService executorService = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), threadFactory, new ThreadPoolExecutor.AbortPolicy());
通过guava的ThreadFactory工厂类还可以指定线程组名称,这对于后期定位错误时也是很有帮助的
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-d%").build();
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池工厂工具
*
* @author 向振华
* @date 2021/04/11 10:24
*/
public class ThreadPoolFactory {
/**
* 生成固定大小的线程池
*
* @param threadName 线程名称
* @return 线程池
*/
public static ExecutorService createFixedThreadPool(String threadName) {
AtomicInteger threadNumber = new AtomicInteger(0);
return new ThreadPoolExecutor(
// 核心线程数
desiredThreadNum(),
// 最大线程数
desiredThreadNum(),
// 空闲线程存活时间
60L,
// 空闲线程存活时间单位
TimeUnit.SECONDS,
// 工作队列
new ArrayBlockingQueue<>(1024),
// 线程工厂
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, threadName + "-" + threadNumber.getAndIncrement());
}
},
// 拒绝策略
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
//尝试阻塞式加入任务队列
executor.getQueue().put(r);
} catch (Exception e) {
//保持线程的中断状态
Thread.currentThread().interrupt();
}
}
}
});
}
/**
* 理想的线程数,使用 2倍cpu核心数
*/
public static int desiredThreadNum() {
return Runtime.getRuntime().availableProcessors() * 2;
}
}
Springboot中使用线程池:
//Springboot中使用线程池
@Configuration
public class ThreadPoolConfig {
@Bean(value = "threadPoolInstance")
public ExecutorService createThreadPoolInstance() {
//通过guava类库的ThreadFactoryBuilder来实现线程工厂类并设置线程名称
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("tp-%d").build();
ExecutorService threadPool = new ThreadPoolExecutor(10, 16, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());
return threadPool;
}
}
//通过name=threadPoolInstance引用线程池实例
@Resource(name = "threadPoolInstance")
private ExecutorService executorService;
@Override
public void spikeConsumer() {
//TODO
executorService.execute(new Runnable() {
@Override
public void run() {
//TODO
}});
}
注意:
- 不允许我们在使用线程池时使用Executors,而是我们自定义ThreadPoolExecutor
销毁:
-
shutdownNow():立即关闭线程池(暴力),正在执行中的及队列中的任务会被中断,同时该方法会返回被中断的队列中的任务列表,将线程池由 RUNNING(运行状态)或SHUTDOWN转换为 STOP状态。
//ExecutorService:List<Runnable> shutdownNow(); public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks; }
-
shutdown():平滑关闭线程池,正在执行中的及队列中的任务能执行完成,后续进来的任务会被执行拒绝策略,将线程池由 RUNNING(运行状态)转换为 SHUTDOWN状态
//ExecutorService:void shutdown(); public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); }
-
isTerminated():当正在执行的任务及队列中的任务全部都执行(清空)完就会返回true
//boolean isTerminated(); public boolean isTerminated() { return runStateAtLeast(ctl.get(), TERMINATED); }
1.3、使用场景:
响应时间优先:
- 用户在饿了么上查看某商家外卖,需要聚合商品库存、店家、价格、红包优惠等等信息返回给用户,接口逻辑涉及到聚合、级联等查询,从这个角度来看接口返回越快越好,那么就可以使用多线程方式,把聚合/级联查询等任务采用并行方式执行,从而缩短接口响应时间。
- 这种场景下使用线程池的目的就是为了缩短响应时间,往往不去设置队列去缓冲并发的请求,而是会适当调高corePoolSize和maxPoolSize去尽可能的创造线程来执行任务。
吞吐量优先:
-
比如业务中台每10分钟就调用接口统计每个系统/项目的PV/UV等指标然后写入多个sheet页中返回,这种情况下往往也会使用多线程方式来并行统计。和"时间优先"场景不同,这种场景的关注点不在于尽可能快的返回,而是关注利用有限的资源尽可能的在单位时间内处理更多的任务,即吞吐量优先。
-
这种场景下我们往往会设置队列来缓冲并发任务,并且设置合理的corePoolSize和maxPoolSize参数,这个时候如果设置了太大的corePoolSize和maxPoolSize可能还会因为线程上下文频繁切换降低任务处理速度,从而导致吞吐量降低。
以上两种使用场景和JVM里的ParallelScavenge和CMS垃圾收集器有较大的类比性,ParallelScavenge垃圾收集器关注点在于达到可观的吞吐量,而CMS垃圾收集器重点关注尽可能缩短GC停顿时间。
1.4、对比ThreadPoolTaskExecutor与ThreadPoolExecutor
-
两者所处jar包不同
-
ThreadPoolTaskExecutor是spring的类,
package org.springframework.scheduling.concurrent
-
ThreadPoolExecutor是JUC下的类
-
-
ThreadPoolTaskExecutor是对ThreadPoolExecutor做了增强,并且ThreadPoolTaskExecutor用到了ThreadPoolTaskExecutor
-
父子类关系,两者顶层接口都是Executor
-
ThreadPoolExecutor:
-
ThreadPoolTaskExecutor:
-
二、线程池任务与优化:
提交任务的方式:
提交方式 | 是否关心返回结果 |
---|---|
Future submit(Callable task) | 是 |
void execute(Runnable command) | 否 |
Future<?> submit(Runnable task) | 否,虽然返回Future,但是其get()方法总是返回null |
execute和submit的区别:
execute和submit都属于线程池的方法,
执行任务分类:
- execute 只能提交ruanable类型任务
- submit既能提交Runnable类型任务也能提交Callable类型任务
有无返回值:
- submit 返回值为Future
- execute 无返回值
异常处理:
- execute会直接抛出任务执行时的异常,可以用try、catch来捕获,和普通线程的处理方式完全一致
- submit会吃掉异常,可通过Future的get方法将任务执行时的异常重新抛出。
不需要返回值的时候统一用execute,大大提升性能
2.1、任务:
Runnable和Callable:
Runnable和Callable都可以理解为任务,里面封装这任务的具体逻辑,用于提交给线程池执行
//JDK1.0
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//JDK1.5
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
使用工具类Executors可以将Runnable转成Callable:
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null) throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
区别:
- 是否有返回值:Runnable任务执行没有返回值,,而Callable可以获取到任务的执行结果返回值
- 是否抛异常:Runnable任务逻辑中不能通过throws抛出cheched异常(但是可以try catch),Callable抛出checked异常, 和Future、FutureTask配合可以用来获取异步执行的结果。
- 是否阻塞:Runnable非阻塞的,Callable阻塞的
- 调用方法不同:void Runnable.run()
,
V Callable.call()` - 执行方式不同:
- Runnable:封装在Thread中,使用Thread的start方法来执行;
- Callable:
- 借助FutureTask,使用Thread的start方法来执行;
- 加入到线程池中,使用线程池的execute或submit执行;
特殊需求:Callable带有返回值的,如果我们不需要返回值
- 必须要return null。
- 使用 Void
threadpool.submit(new Callable<Void>() {
@Override
public Void call() {
//...
return null;
}
});
Future和FutureTask:
Future接口:
- 表示执行异步任务的结果存储器
- 当一个任务的执行时间过长就可以采用这种方式:把任务提交给子线程去处理,主线程不用同步等待,当向线程池提交了一个Callable或Runnable任务时就会返回Future,
- 用Future可以获取任务执行的返回结果
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public interface Future<V> {
//取消任务,boolean类型入参表示如果任务正在运行中是否强制中断;
boolean cancel(boolean mayInterruptIfRunning);
//判断任务是否被取消;
boolean isCancelled();
//判断任务是否执行完毕,执行完毕不代表任务一定成功执行,比如任务执行失但也执行完毕、任务被中断了也执行完毕都会返回true,
//它仅仅表示一种状态说后面任务不会再执行了;
boolean isDone();
//返回任务的执行结果,
//若任务还未执行完,则会一直阻塞直到完成为止,
//如果执行过程中发生异常,则抛出异常,但是主线程是感知不到并且不受影响的,除非调用get()方法进行获取结果则会抛出ExecutionException异常;
V get() throws InterruptedException, ExecutionException;
//在指定时间内返回任务的执行结果,超时未返回会抛出TimeoutException,这个时候需要显式的取消任务;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
测试;
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
Future<Integer> future = executorService.submit(new Task());
Integer integer = future.get();
System.out.println(integer);
executorService.shutdown();
}
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程开始计算");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
FutureTask:
package java.util.concurrent;
import java.util.concurrent.locks.LockSupport;
/**
* @since 1.5
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public class FutureTask<V> implements RunnableFuture<V> {
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {
return state >= CANCELLED;
}
public boolean isDone() {
return state != NEW;
}
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
protected void done() { }
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected boolean runAndReset() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt
}
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
private void removeWaiter(WaitNode node) {
if (node != null) {
node.thread = null;
retry:
for (;;) { // restart on removeWaiter race
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
if (q.thread != null)
pred = q;
else if (pred != null) {
pred.next = s;
if (pred.thread == null) // check for race
continue retry;
}
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
q, s))
continue retry;
}
break;
}
}
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long stateOffset;
private static final long runnerOffset;
private static final long waitersOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = FutureTask.class;
stateOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("state"));
runnerOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));
waitersOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("waiters"));
} catch (Exception e) {
throw new Error(e);
}
}
}
测试;
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
executorService.submit(futureTask);
Integer integer = futureTask.get();
System.out.println(integer);
executorService.shutdown();
}
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程开始计算");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
//知识库页面发布接口逻辑
@Override
public ResultResponse publish(PageIndex page) {
ResultResponse<Boolean> result = new ResultResponse<>();
ResultResponse<PageIndex> recordResult = getPageRecord(page.getPageId());
if (!recordResult.isSuccess()) {
return recordResult;
}
PageIndex record = recordResult.getData();
String currentUser = RequestContext.getUser().getUserCode();
// 生成html文件,后续导出pdf要用
FutureTask htmlFileTask = new FutureTask<>(new CreateHtmlFileTask(page.setName(record.getName())));
// 同步更新页面所属知识库的最后更新人及更新时间
FutureTask wikiUpdateTask = new FutureTask<>(new WikiUpdateTask(record.getWikiId(), currentUser));
// 生成页面历史版本记录
FutureTask pageVersionTask = new FutureTask<>(new GeneratePageVersionTask(new PageVersion(record.getPageId(), record.getWikiId(), page.getHtmlContent(), page.getContent(), currentUser, new Date(), null, null)));
// 更新页面最新信息
FutureTask pageUpdateTask = new FutureTask<>(new PageUpdateTask(page));
List<FutureTask<String>> futureTaskList = ImmutableList.of(htmlFileTask, wikiUpdateTask, pageVersionTask, pageUpdateTask);
// 任务提交
futureTaskList.forEach(task -> executorService.submit(task));
try {
for (FutureTask<String> task : futureTaskList) {
// 阻塞式等待任务执行结果
String taskResult = task.get();
logger.info("publish taskResult={}", taskResult);
}
} catch (Exception e) {
logger.error("发布接口异常:{}", e, e.getMessage());
e.printStackTrace();
}
// 释放锁
pageLockService.unlock(page.getPageId(), currentUser);
return result.data(true);
}
class CreateHtmlFileTask implements Callable<String> {
private PageIndex pageIndex;
public CreateHtmlFileTask(PageIndex pageIndex) {
this.pageIndex = pageIndex;
}
@Override
public String call() throws Exception {
return generateHtmlFile(pageIndex);
}
}
2.2、优化:
ThreadPoolExecutor执行逻辑:
- 提交新任务
- 判断核心线程数是否已满,如果已满走3,否则直接创建新线程
- 判断队列是否已满,如果已满,走4,否则任务放入队列
- 判断线程池数量是否大于maxsize,大于走5,否则创建新线程执行该任务
- 执行拒绝策略
缺点:
- core线程一般不会timeout
- 新任务提交时,如果工作线程小于核心线程,会自动创建线程,即使当前工作线程已经空闲,会造成空闲线程浪费
- 设置的maxsize只有在队列满时,才会生效,默认情况容器队列很大。
例子:一个coreSize为10,maxSize为100,队列长度为1000的线程池,在运行一段时间之后的效果会是以下2个效果:
- 系统空闲时,线程池中始终保持10个线程不变,有一部分线程在执行任务,另一部分线程一直wait中(即使设置allowCoreThreadTimeOut)
- 系统繁忙时,线程池中线程仍然为10个,但队列中有还没有执行的任务(不超过1000),存在任务堆积现象
简单版本线程池:
- 新任务提交时,有空闲线程,直接执行任务,不要创建新线程
- 如果coresize满了。且线程数小于maxsize,则优先创建线程,不是放入队列
- 其他规则类似于ThreadPoolExecutor,如 timeOut机制
执行线程的任务执行逻辑:不断从队列中获取任务并执行,即只要有执行线程,直接往队列中放任务,执行线程就被通知到并直接执行任务
空闲线程优先:
线程数小于核心线程数,如果有空闲线程,不去创建线程、将任务放入队列中,
直接调整默认的ThreadPoolExecutor逻辑,通过重载 execute(Runnable) 方法达到效果
public void execute(Runnable command) {
//此处优先处理有活跃线程的情况,避免在<coreSize时,直接创建线程
if(getActiveCount() < getPoolSize()) {
if(pool1.offer(command)) {
return;
}
}
super.execute(command);
}
coreSize满了优先创建线程
-
从之前的逻辑来看,如果放入队列失败,则尝试创建新线程。在这个时候,相应的coreSize肯定已经满了。那么,只需要处理一下逻辑,将其offer调整为false,即可以实现相应的目的。
public boolean offer(Runnable o) { //这里的parent为ThreadPoolExecutor的引用 int poolSize = parent.getPoolSize(); int maxPoolSize = parent.getMaximumPoolSize(); //还没到最大值,先创建线程 if(poolSize < maxPoolSize) { return false; } //默认逻辑 return super.offer(o); }
总结:
- 按照以上的调整,只需要通过继承自默认的ThreadPoolExecutor和默认的BlockingQueue(如LinkedBlockingDeque),重载2个主要的方法 ThreadPoolExecutor#execute 和 LinkedBlockingDeque#offer 即达到调整的目的。
- 缺点在于,实现后的类,在定义时,需要互相引用,因为相应的逻辑中需要互相调用相应的方法,以处理逻辑。此外,ThreadPoolExecutor的相应方法 getXXX 方法,在调用时都为通过加锁式实现,以精确返回数据,这里在多线程环境中可能会存在一些性能上的考虑。
- 在Tomcat默认的worker线程池中,即是采用以上的逻辑来达到工作线程的调整逻辑。因此在spring boot tomcat embedded中,通过参数 server.tomcat.max-thread 和 min-thread 即是通过优先调整线程来达到控制工作线程的目的。 相应的处理类为 org.apache.tomcat.util.threads 下的 ThreadPoolExecutor 和 TaskQueue。
三、源码解析:
3.1、Executor、ExecutorService接口和ThreadPoolExecutor类
Executor:
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
*/
public interface Executor {
/**
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
ExecutorService:
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
*/
public interface ExecutorService extends Executor {
void shutdown();//在完成已提交的任务后关闭服务,不再接受新任;
List<Runnable> shutdownNow();//停止所有正在执行的任务并关闭服务;
boolean isShutdown();//测试是否该ExecutorService已被关闭。
boolean isTerminated();//测试是否所有任务都执行完毕了;
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
//提交Callable任务到线程池,返回Future对象,调用Future对象get()方法可以获取Callable的返回值;
<T> Future<T> submit(Callable<T> task);
// 提交Runnable任务到线程池,返回Future对象,由于Runnable没有返回值,也就是说调用Future对象get()方法返回null;
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
/**
* invokeAll会按照任务集合中的顺序将所有的Future添加到返回的集合中,该方法是一个阻塞的方法。
* 只有当所有的任务都执行完毕时,或者调用线程被中断,又或者超出指定时限时,invokeAll方法才会返回。
* 当invokeAll返回之后每个任务要么返回,要么取消,此时客户端可以调用get/isCancelled来判断具体是什么情况。
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 阻塞的方法,不会返回 Future 对象,而是返回集合中某一个Callable 对象的结果,而且无法保证调用之后返回的结果是哪一个 Callable,
* 如果一个任务运行完毕或者抛出异常,方法会取消其它的 Callable 的执行。
* 和invokeAll区别:只要有一个任务执行完了,就把结果返回,并取消其他未执行完的任务;
* 同样,也带有超时功能;
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
AbstractExecutorService:
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
*/
public abstract class AbstractExecutorService implements ExecutorService {
//根据线程新建任务
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
//根据线程新建任务重载
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
//根据线程提交任务
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);//线程执行任务
return ftask;
}
根据线程提交任务
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
//根据线程提交任务
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null) throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0) throw new IllegalArgumentException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
ExecutorCompletionService<T> ecs =new ExecutorCompletionService<T>(this);
try {
ExecutionException ee = null;
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Iterator<? extends Callable<T>> it = tasks.iterator();
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1;
for (;;) {
Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
else if (active == 0) break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null) throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
else f = ecs.take();
}
if (f != null) {
--active;
try { return f.get();
} catch (ExecutionException eex) { ee = eex;
} catch (RuntimeException rex) { ee = new ExecutionException(rex);
}
}
}
if (ee == null) ee = new ExecutionException();
throw ee;
} finally {
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try {
return doInvokeAny(tasks, false, 0);
} catch (TimeoutException cannotHappen) {
assert false;
return null;
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return doInvokeAny(tasks, true, unit.toNanos(timeout));
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
if (tasks == null) throw new NullPointerException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
throws InterruptedException {
if (tasks == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks)
futures.add(newTaskFor(t));
final long deadline = System.nanoTime() + nanos;
final int size = futures.size();
for (int i = 0; i < size; i++) {
execute((Runnable)futures.get(i));
nanos = deadline - System.nanoTime();
if (nanos <= 0L)
return futures;
}
for (int i = 0; i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
if (nanos <= 0L)
return futures;
try {
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
return futures;
}
nanos = deadline - System.nanoTime();
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
}
ThreadPoolExecutor:
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
*/
public class ThreadPoolExecutor extends AbstractExecutorService {
//原子操作的Integer类
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//-536870912
private static final int COUNT_BITS = Integer.SIZE - 3;//32-3=29
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//536870912-1 2^29-1 容量
// 线程池五种状态
//SHUTDOWN 状态 和 STOP 状态 先会转变为 TIDYING 状态,最终都会变为 TERMINATED
private static final int RUNNING = -1 << COUNT_BITS;//允许提交并处理任务 -536870912
private static final int SHUTDOWN = 0 << COUNT_BITS;//不允许提交新的任务,但会处理已提交的任务 0
private static final int STOP = 1 << COUNT_BITS;//不允许提交新的任务,也不会阻塞队列中未执行的任务 536870912
private static final int TIDYING = 2 << COUNT_BITS;//所有任务执行完毕,池中工作线程数为0,等待执行terminated()勾子方法 1073741824 2^30
private static final int TERMINATED = 3 << COUNT_BITS;//terminated()勾子方法执行完毕 1610612736
private final ReentrantLock mainLock = new ReentrantLock();
private final HashSet<Worker> workers = new HashSet<Worker>();//工作线程set集合
private final Condition termination = mainLock.newCondition();//支持 awaitTermination 的等待条件
private int largestPoolSize;//跟踪获得的最大池大小。仅在以下访问
/**
* 完成任务的计数器。仅在终止工作线程时更新。只能在 mainLock 下访问。
*/
private long completedTaskCount;
/**
* 如果为 false(默认),核心线程即使在空闲时也保持活动状态。
* 如果为真,核心线程使用 keepAliveTime 超时等待工作。
*/
private volatile boolean allowCoreThreadTimeOut;
private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();//默认拒绝策略:丢弃该任务并抛出异常
private static final RuntimePermission shutdownPerm =new RuntimePermission("modifyThread");//
private final AccessControlContext acc;
//核心参数:
private volatile int corePoolSize;//核心线程数量
/**
* 最大池大小。请注意,实际最大值在内部受 CAPACITY 限制。
*/
private volatile int maximumPoolSize;
private volatile long keepAliveTime;//非核心线程的空闲时间,超过keepAliveTime自动终止并回收
private final BlockingQueue<Runnable> workQueue;//阻塞队列
private volatile ThreadFactory threadFactory;//线程工厂
private volatile RejectedExecutionHandler handler;//拒绝策略
// 打包和解包ctl
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; }
private static boolean runStateLessThan(int c, int s) { return c < s;}
private static boolean runStateAtLeast(int c, int s) { return c >= s;}
private static boolean isRunning(int c) { return c < SHUTDOWN; }//是否为运行态
//CAS 自增1
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
//CAS 自减1
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
//Worker继承AQS 实现了Runable
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
xxx
}
}
设置核心线程的方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VintMUi8-1654824775385)(线程池.assets/image-20220609160104539.png)]
- getCorePoolSize():返回线程池的核心线程数,这个值是一直不变的,返回在构造函数中设置的coreSize大小;
- getMaximumPoolSize():返回线程池的最大线程数,这个值是一直不变的,返回在构造函数中设置的maximumPoolSize大小
- getLargestPoolSize():记录了曾经出现的最大线程个数(水位线);
- getPoolSize():线程池中当前线程的数量;
- getActiveCount():Returns the approximate(近似) number of threads that are actively executing tasks;
- prestartAllCoreThreads():会启动所有核心线程,无论是否有待执行的任务,线程池都会创建新的线程,直到池中线程数量达到 corePoolSize;
- prestartCoreThread():会启动一个核心线程(同上);
- allowCoreThreadTimeOut(true):允许核心线程在KeepAliveTime时间后,退出;
3.2、线程池实现线程复用的原理:
线程池执行的是任务,核心逻辑在ThreadPoolExecutor类的execute方法,这个方法是间接实现了Executor里面的execute方法,同时ThreadPoolExecutor中维护了HashSet workers;
//开发人员去重写方法体实现自己的业务逻辑,非常适合做钩子函数
protected void beforeExecute(Thread t, Runnable r) { }
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();//如果线程为空,抛空指针异常
/*
分三步:
1. 如果运行的线程少于 corePoolSize,尝试 使用给定命令启动一个新线程作为它的第一个任务。对 addWorker 的调用以原子方式检查 runState 和 workerCount,因此通过返回 false 来防止错误警报会在不应该添加线程时添加线程。
2. 如果一个任务可以成功排队,那么我们仍然需要仔细检查我们是否应该添加一个线程(因为现有的线程自上次检查后死亡)或者自从进入此方法后池关闭.所以我们 重新检查状态,如果停止则回滚入队,如果没有,则启动一个新线程。
3. 如果我们不能排队任务,那么我们尝试添加一个新的线程。如果它失败了,我们知道我们已经关闭或饱和,因此拒绝任务。
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {//工作线程小于核心线程数量时
//addWorker方法, param 如果 true 使用 corePoolSize 作为绑定,否则 maximumPoolSize。 (此处使用布尔指示符而不是值来确保在检查其他池状态后读取新值)。
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);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
//开发人员去重写方法体实现自己的业务逻辑,非常适合做钩子函数,在任务run方法的前后增加业务逻辑,比如添加日志、统计等。这个和我们springmvc中拦截器的preHandle和afterCompletion方法很类似,都是对方法进行环绕,类似于spring的AOP
protected void afterExecute(Runnable r, Throwable t) { }
-
addWorker方法:创建执行任务,如果是核心线程的任务,会赋值给Worker的firstTask属性
-
Worker实现了Runnable,本质上也是任务,核心方法在run()
-
run方法执行核心runWorker,自旋拿任务,while (task != null || (task = getTask()) != null)),task是核心线程Worker的firstTask或者getTask();
-
getTask()的核心逻辑:
-
若当前工作线程数量大于核心线程数->说明此线程是非核心工作线程,通过poll()拿任务,未拿到任务即getTask()返回null,然后会在processWorkerExit(w, completedAbruptly)方法释放掉这个非核心工作线程的引用;
-
若当前工作线程数量小于核心线程数->说明此时线程是核心工作线程,通过take()拿任务
-
take()方式取任务,如果队列中没有任务了会调用await()阻塞当前线程,直到新任务到来,所以核心工作线程不会被回收; 当执行execute方法里的workQueue.offer(command)时会调用Condition.singal()方法唤醒一个之前阻塞的线程,这样核心线程即可复用
-