执行器框架的应用
ThreadPoolExecutor
ThreadPoolExecutor类继承了AbstractExecutorService类,实现的ExecutorService接口,同时ExecutorService继承Executor方法
submit方法,支持Runnable、Callable参数, 返回Future对象用来处理返回结果
invokeAny和invokeAll方法,接收一个Collection集合对象,invokeAny方法表示集合中任何一个线程返回结果即返回,invokeAll方法返回所有执行结果
ThreadPoolExecutor为我们提供了4种构造函数,最多的7个参数,我们来了解这7个参数的作用
线程池的7个参数
corePoolSize:核心线程池数
maximumPoolSize:最大线程池数量
-
当在execute(Runnable)方法中提交新任务并且少于corePoolSize线程正在运行时,即使其他工作线程处于空闲状态,也会创建一个新线程来处理该请求。
-
如果有多于corePoolSize但小于maximumPoolSize线程正在运行,则仅当队列已满时才会创建新线程。
-
如果队列已满,maximumPoolSize达到最大值,则执行拒绝策略
keepAliveTime 和 unit:设置非corePoolSize线程的存活时间,unit是时间单位,当线程处于空闲状态,并且超出corePoolSize的线程,将在指定时间内被回收
workQueue:工作队列,提交到线程的工作任务,workQueue的大小与线程数量有一定关系
- 当workQueue数量小于corePoolSize,则直接创建新线程
- 当workQueue数量大于corePoolSize,小于maximumPoolSize,则放到队列等待执行
- 当队列已满,创建新线程执行,当达到maximumPoolSize,则新任务执行拒绝策略
threadFactory:线程工厂,用于创建新线程
handler:执行拒绝策略,默认支持4种策略,也可以实现RejectedExecutionHandler自定义策略
- AbortPolicy:默认测策略,抛出RejectedExecutionException运行时异常;
- CallerRunsPolicy:使用调用者执行任务,会导致主线程堵塞,影响后续调度
- DiscardPolicy:直接丢弃新提交的任务;
- DiscardOldestPolicy:如果执行器没有关闭,队列头的任务将会被丢弃,然后执行器重新尝试执行任务(如果失败,则重复这一过程);
Executors 创建线程池
Executors 提供一系列工厂方法用于创建线程池,底层是使用ThreadPoolExecutor创建
- newFixedThreadPool创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看出corePoolSize和maximumPoolSize数量一致,即固定保存nThreads个线程,使用LinkedBlockingQueue无边界队列,需要注意内存爆满的问题
- newSingleThreadExecutor创建单个线程的执行器
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
可以看出依然使用的ThreadPoolExecutor创建线程池,但是不一样的是又包装了FinalizableDelegatedExecutorService类,那它有什么作用呢?
通过FinalizableDelegatedExecutorService包装后,它去掉了如setCorePoolSize方法,很巧妙的保证后续,不可以改变线程池的个数
这也是newFixedThreadPool(1) 和 newSingleThreadExecutor()本质的区别,newFixedThreadPool可以通过setCorePoolSize修改线程大小
- newCachedThreadPool带缓存线程的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可以看出核心线程数是0,maximumPoolSize是Integer最大值,keepAliveTime是60s,也就是说60s内线程会被重用,线程数量随着任务数量不断增加,因此要注意避免执行任务时间过长的,适合执行大量的运行时间短的任务
- newScheduledThreadPool 周期性线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。
ScheduledThreadPool:核心线程池固定,大小无限的线程池。
此线程池支持定时以及周期性执行任务的需求。
线程池的状态
线程池有Runnable、Shutdown、Stop、Tidying、Terminated五种状态
Runnable
状态说明:当处于Runnable状态,线程可正常处理和添加新任务
切换说明:当线程池创建成功后,线程进入Runnable状态
Shutdown:
状态说明:当处于Shutdown状态,线程只处理队列中的任务,不接受新内容
切换说明:当调用shutdown方法后,线程池由RUNNING -> SHUTDOWN。
Stop:
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
切换说明:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
Tidying:
状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
切换说明:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
terminated:
状态说明:线程池彻底终止,就变成TERMINATED状态。
切换说明:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
本文如有任何问题,请留言指教,以免误导他人