基于JDK1.8源码进行分析的。
首先我们看一下Executor接口的源码。
public interface Executor {
void execute(Runnable command);
}
该接口定义了一个规范,也就是execute方法的规范,里面需要传入Runnable类或者其子类的对象。该接口有一个实现接口,ExecutorService。下面我们看一下该接口的源码。
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<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;
<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;
}
相比Executor,该ExecutorService接口定义了更多的规范,里面有两个核心方法,分别是:
- Future<?> submit(Runnable task);
- <T> Future<T> submit(Callable<T> task);
两者的区别在于是否有返回结果,很明显,Callable有返回结果。他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。在没有Future之前我们检测一个线程是否执行完毕通常使用Thread.join()或者用一个死循环加状态位来描述线程执行完毕。现在有了更好的方法能够阻塞线程,检测任务执行完毕甚至取消执行中或者未开始执行的任务。
ScheduledExecutorService接口定义了一些方法或者说是规范。下面是关于这个接口的源码。
public interface ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
一共有四个方法,都是方便用户针对执行时间,进行更细化的操作和控制。看到这里我想到了定时JOB,一共有以下几种方式进行创建。
- 多线程
- 线程池
- Timer/TimerTask
- spring的框架quartz
- springBoot自带的Schedule
一共有以上几种实现方式,而ScheduledExecutorService接口里面定义的方法,很类似于Timer/TimerTask的使用。
ThreadPoolExecutor是ExecutorService的一个实现,当然是继承自AbstractExecutorService抽象类。
public class ThreadPoolExecutor extends AbstractExecutorService
ScheduledThreadPoolExecutor是继承自ThreadPoolExecutor,实现了ScheduledExecutorService接口。
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
总结来看如下图:
![]() |
说了这么多还没扯到正题,因为配置一个线程池还是需要了解很多的,比如说:策略等,并且有时候我们即使配置了,但不一定是最优的。我们都知道java一般都会定义一些工具类方法,方便用户操作。比如常见到的有:Arrays、Collections、Objects等。我们的Executor也有一个工具类,叫做Executors。一般我们也是通过该类来创建线程池,既方便用户创建,也方便用户进行操作。该工具类中我们常用的有五种线程池,分别如下:
- newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- newScheduledThreadPool:创建一个大小无限制的定时线程池。此线程池支持定时以及周期性执行任务的需求。
- newSingleThreadScheduledExecutor:创建一个单线程的定时线程池。此线程池支持定时以及周期性执行任务的需求。
下面我们分别看一下这五个方法。
newSingleThreadExecutor
创建一个单线程的线程池。
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));
}
newFixedThreadPool
创建固定大小的线程池。
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);
}
newCachedThreadPool
创建一个可缓存的线程池。
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);
}
newScheduledThreadPool
创建一个大小无限制的定时线程池。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
newSingleThreadScheduledExecutor
创建一个单线程的定时线程池。
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}
总体来看,分别都是有一个重载方法,一个方法用来默认创建,或者指定参数,另外一个重载方法都是通过ThreadFactory进行创建,而且很明显可以看出,基本一种类型的线程池会有对应的实现方式,实现类。
关于线程池的状态,源码中有如下内容:
RUNNING: Accept new tasks and process queued tasks
SHUTDOWN: Don't accept new tasks, but process queued tasks
STOP: Don't accept new tasks, don't process queued tasks,
and interrupt in-progress tasks
TIDYING: All tasks have terminated, workerCount is zero,
the thread transitioning to state TIDYING
will run the terminated() hook method
TERMINATED: terminated() has completed
可以看到一共有五种状态可以看到。
- Running:接收新的任务,处理队列任务。
- Shutdown:不接受新任务,但处理队列任务。
- Stop:不接受新任务,不处理队列任务,并且中断正在执行的任务。
- Tidying:所有的任务都终止。转换到状态TIDYING的线程将运行terminate()钩子方法
- Terminated:terminated()已经完成。
下面我们看一下状态转化:
RUNNING -> SHUTDOWN
为了避免理解产生偏差,以下是源码中的注释内容。
On invocation of shutdown(), perhaps implicitly in finalize()
在调用shutdown()时,可能隐含在finalize()中
(RUNNING or SHUTDOWN) -> STOP
On invocation of shutdownNow()
当调用shutdownNow()
SHUTDOWN -> TIDYING
When both queue and pool are empty
当队列,线程池都空的时候
STOP -> TIDYING
When pool is empty
当线程池空的时候
TIDYING -> TERMINATED
When the terminated() hook method has completed
当terminated()钩子函数调用完成的时候
这么看可能比较笼统,我们下面用图表示一下。
![]() |
我觉得还算比较清晰。
下篇我们先分析一下ThreadPoolExecutor源码。这篇先到这里。