Java并发之任务执行器Executor
一、概述
以前线程Thread既表示执行的任务,又表示执行的机制。在JDK1.5中,java并发框架提供了一种“执行服务”的相关API,它将"任务的执行"和"任务的提交“相分离,”执行服务“封装了任务执行的细节,对于任务提交者来说,它可进一步聚焦于任务本身,如任务提交、获取任务执行后的结果、取消任务而不需要关注任务执行的细节,如线程的创建、任务的调试、线程的复用或关闭等。

- Executor接口:这个接口也是整个线程池中最顶层的接口,提供了一个无返回值的提交任务的方法。
- ExecutorService接口:派生自
Executor接口,扩展了很过功能,例如关闭线程池,提交任务并返回结果数据、唤醒线程池中的任务等。 - AbstractExecutorService抽象类:派生自
ExecutorService接口,实现了几个非常实现的方法,供子类进行调用。 - ScheduledExecutorService定时任务接口,派生自
ExecutorService接口,拥有ExecutorService接口定义的全部方法,并扩展了定时任务相关的方法。
二、Executor接口
Executor接口也只有一个方法,这个方法接受一个Runnable类型的参数,这是个抽象方法,它无法指定任务该如何执行。它可能是新建一个线程执行任务,也可能是利用线程池中的一个线程,还可能是在调用者线程中执行。
public interface Executor {
//提交运行任务,参数为Runnable接口对象,无返回值
void execute(Runnable command);
}
三、ExecutorService接口
ExecutorService扩展了Executor接口,相较于Executor,主要多了两个新特性:1、手动关闭;2、支持Callable和Future接口。
package java.util.concurrent;
import java.util.List;
import java.util.Collection;
public interface ExecutorService extends Executor {
//关闭线程池,线程池中不再接受新提交的任务,但是之前提交的任务继续运行,直到完成
void shutdown();
//关闭线程池,线程池中不再接受新提交的任务,会尝试停止线程池中正在执行的任务。
List<Runnable> shutdownNow();
//判断线程池是否已经关闭
boolean isShutdown();
//判断线程池中的所有任务是否结束,只有在调用shutdown或者shutdownNow方法之后调用此方法才会返回true。
boolean isTerminated();
//等待线程池中的所有任务执行结束,并设置超时时间
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
//提交一个Callable接口类型的任务,返回一个Future类型的结果
<T> Future<T> submit(Callable<T> task);
//提交一个Callable接口类型的任务,并且给定一个泛型类型的接收结果数据参数,返回一个Future类型的结果
<T> Future<T> submit(Runnable task, T result);
//提交一个Runnable接口类型的任务,返回一个Future类型的结果
Future<?> submit(Runnable task);
//批量提交任务并获得他们的future,Task列表与Future列表一一对应
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
//批量提交任务并获得他们的future,并限定处理所有任务的时间
<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;
}
3.1 手动关闭
①.ExecutorService有两个关闭执行服务的方法,分别是shutdown和shutdownNow。shutdown和shutdownNow不会阻塞等待,它们返回后不代表所有任务都结束。
shutdown方法调用后不会接受新的任务,但已提交的任务将继续执行(即使任务还未真正开始执行);shutdownNow方法不仅不会接受新任务,而且还会终止已经提交但未执行的任务,对于正在执行的任务,一般调用Thread.interrupt()方法设置中断标志,不过线程可能不响应中断,shutdownNow会返回已提交但未执行的任务列表。
②.isShutdown返回执行服务是否被关闭的布尔值(不会等待),只要shutdown或shutdownNow任意一方法被调用后,isShutdown都将返回true。
③.awaitTermination方法用于等待执行服务中的所有任务完成,此方法需要设置超时时间,如果在限时间内所有任务都结束了(允许非正常结束),
④.isTerminated返回在执行服务关闭后所有任务是否已完成的布尔值,如果在此之前shutdownNow或shutdown没有被调用,这里永不可能返回true。
3.2 支持Callable和Future接口
①.三个submit方法都用于提交单任务
submit(Callable<T>)方法中入参Callable本身有返回结果;submit(Runnable, T)方法在设定任务的同时可以提供一个结果,在任务结束时将返回这个结果;submit(Runnable)方法入参没有提供结果,最终返回的结果是null。
②.ExecutorService有两类批量提交任务的方法,invokeAll和invokeAny,它们都有两个版本,一个不限时版本、一个超时版本。
invokeAny:批量提交任务,只要有一个任务执行完成,就返回这个任务的结果,如果所有任务都未执行完成,则抛出异常。invokeAll:批量提交任务,并返回一个List集合,其中包含每个任务的Future对象;
3.3 示例1
分别实现以下三种执行器的关闭方式:
- 方式1:
Executor,无法主动关闭,根据创建线程池的方式的不同而采取不同的关闭策略 - 方式2:
ExecutorService.shutdown(),不再接收新的任务,在当前已经提交的任务执行完毕后,关闭执行器。 - 方式3:
ExecutorService.shutdownNow(),不再接收新的任务,立刻关闭执行器,如果存在正在运行的任务,则终止这些任务。
public class Test {
public static void main(String[] args) {
int type = 0;
switch (type) {
case 0:
//Executor无法主动关闭 根据创建线程池的方式采取不同的关闭策略
Executor executor = Executors.newCachedThreadPool();
//Executor只有一个方法execute():提交Runnable任务
executor.execute(getRunnable("executor"));
break;
case 1:
//ExecutorService shutdown 当正在运行的线程运行结束后,关闭线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//ExecutorService通过submit提交任务
executorService.submit(getRunnable("executorService"));
executorService.shutdown();
break;
case 2:
//ExecutorService shutdownNow 理解关闭线程池,正在运行的线程将被停止
ExecutorService executorService1 = Executors.newCachedThreadPool();
//ExecutorService通过submit提交任务
executorService1.submit(getRunnable("executorService"));
executorService1.shutdownNow();
break;
default:
break;
}
}
private static Runnable getRunnable(String executorName) {
return () -> {
String name = Thread.currentThread().getName();
System.out.println(name + "--- " + executorName + " begin ...");
try {
Thread.sleep(1000);
System.out.println(name + "--- " + executorName + " end .");
} catch (InterruptedException e) {
System.out.println(name + "--- " + executorName + " is interrupted .");
}
};
}
}
方式1(Executor)运行结果:
pool-1-thread-1--- Executor begin ...
pool-1-thread-1--- Executor end .
执行器会在线程运行完之后,再等待60s之后再关闭。
方式2(shutdown)运行结果:
pool-1-thread-1--- ExecutorService begin ...
pool-1-thread-1--- ExecutorService end .
执行器会在线程运行完之后再关闭。
方式3(shutdownNow)运行结果:
pool-1-thread-1--- ExecutorService begin ...
pool-1-thread-1--- ExecutorService is interrupted .
执行器会在线程运行过程中关闭。
3.4 示例2
- 定义一个任务(
Callable)集合,其中的每个任务实现随机取数。 - 定义一个结果(
Future)集合,与任务(Callable)集合相对应。 - 实现1:批量执行任务集合,当所有的任务执行完毕之后,输出所有的执行结果。
- 实现2:批量执行任务集合,输出第一个执行完毕的任务的执行结果。
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Callable<Integer>> callableList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
callableList.add(() -> {
Integer result = ThreadLocalRandom.current().nextInt(100, 2000);
Thread.sleep(result);
System.out.println("init " + result);
return result;
});
}
//全部任务完成
List<Future<Integer>> futureList = executorService.invokeAll(callableList);
System.out.println("等待所有的任务完成之后,才会得到结果集。");
futureList.forEach(future -> {
try {
System.out.println("result " + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
Thread.sleep(1000);
System.out.println("-----------");
//最先完成任务
Integer firstResult = executorService.invokeAny(callableList);
System.out.println("得到一个最先得到的结果,立即返回;后面的任务不再运行。");
System.out.println("result " + firstResult);
executorService.shutdown();
}
}
invokeAny会导致未执行完成的任务不再执行。
init 139
init 172
init 667
init 1776
init 1806
等待所有的任务完成之后,才会得到结果集。
result 1806
result 667
result 139
result 1776
result 172
-----------
init 595
得到一个最先得到的结果,立即返回;后面的任务不再运行。
result 595
四、AbstractExecutorService抽象类
AbstractExecutorService类是一个抽象类,派生自ExecutorService接口,在其基础上实现了几个比较实用的方法,提供给子类进行调用。我们还是来看下AbstractExecutorService类的源码。
4.1 newTaskFor方法
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);
}
RunnableFuture类用于获取执行结果,在实际使用时,我们经常使用的是它的子类FutureTask,newTaskFor方法的作用就是将任务封装成FutureTask对象,后续将FutureTask对象提交到线程池。
4.2 doInvokeAny方法
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对象
// ExecutorCompletionService负责执行任务,后面调用用poll返回第一个执行结果
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集合中
//提交一个任务主要是确保在进入循环之前开始一个任务
futures.add(ecs.submit(it.next()));
--ntasks;
//记录正在执行的任务数量
int active = 1;
for (;;) {
//从完成任务的BlockingQueue队列中获取并移除下一个将要完成的任务的结果。
//如果BlockingQueue队列中中的数据为空,则返回null
//这里的poll()方法是非阻塞方法
Future<T> f = ecs.poll();
//获取的结果为空
if (f == null) {
//集合中仍有未执行的任务数量
if (ntasks > 0) {
//未执行的任务数量减1
--ntasks;
//提交完成并将结果添加到futures集合中
futures.add(ecs.submit(it.next()));
//正在执行的任务数量加•1
++active;
}
//所有任务执行完成,并且返回了结果数据,则退出循环
//之所以处理active为0的情况,是因为poll()方法是非阻塞方法,可能导致未返回结果时active为0
else if (active == 0)
break;
//如果timed为true,则执行获取结果数据时设置超时时间,也就是超时获取结果表示
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
//没有设置超时,并且所有任务都被提交了,则一直阻塞,直到返回一个执行结果
else
f = ecs.take();
}
//获取到执行结果,则将正在执行的任务减1,从Future中获取结果并返回
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);
}
}
这个方法是批量执行线程池的任务,最终返回一个结果数据的核心方法,通过源代码的分析,我们可以发现,这个方法只要获取到一个结果数据,就会取消线程池中所有运行的任务,并将结果数据返回。这就好比是很多要进入一个居民小区一样,只要有一个人有门禁卡,门卫就不再检查其他人是否有门禁卡,直接放行。
在上述代码中,我们看到提交任务使用的ExecutorCompletionService对象的submit方法,我们再来看下ExecutorCompletionService类中的submit方法,如下所示。
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
}
可以看到,ExecutorCompletionService的submit方法有两个重载方法,一个是提交Callable任务,一个是提交Runnable任务,并且都返回Future对象。
4.3 invokeAny方法
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));
}
这两个invokeAny方法本质上都是在调用doInvokeAny方法,在线程池中提交多个任务,只要返回一个结果数据即可。
直接看上面的代码,大家可能有点晕。这里,我举一个例子,我们在使用线程池的时候,可能会启动多个线程去执行各自的任务,比如线程A负责task_a,线程B负责task_b,这样可以大规模提升系统处理任务的速度。如果我们希望其中一个线程执行完成返回结果数据时立即返回,而不需要再让其他线程继续执行任务。此时,就可以使用invokeAny方法。
4.4 invokeAll方法
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对象提交任务
RunnableFuture<T> f = newTaskFor(t);
//将结果数据添加到futures集合中
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 {
//如果发生中断异常InterruptedException 则取消已经提交的任务
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));
// 在添加执行任务时超时判断,如果超时则立刻返回futures集合
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);
}
}
invokeAll方法同样实现了无超时时间设置和有超时时间设置的逻辑。
无超时时间设置的invokeAll方法总体逻辑为:将所有任务封装成RunnableFuture对象,调用execute方法执行任务,将返回的结果数据添加到futures集合,之后对futures集合进行遍历判断,检测任务是否完成,如果没有完成,则调用get方法阻塞任务,直到返回结果数据,此时会忽略异常。最终在finally代码块中对所有任务是否完成的标识进行判断,如果存在未完成的任务,则取消已经提交的任务。
有超时设置的invokeAll方法总体逻辑与无超时时间设置的invokeAll方法总体逻辑基本相同,只是在两个地方添加了超时的逻辑判断。一个是在添加执行任务时进行超时判断,如果超时,则立刻返回futures集合;另一个是每次对结果数据进行判断时添加了超时处理逻辑。
invokeAll方法中本质上还是调用Executor接口的execute方法来提交任务。
4.5 submit方法
submit方法的逻辑比较简单,就是将任务封装成RunnableFuture对象并提交,执行任务后返回Future结果数据。如下所示。
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;
}
从源码中可以看出submit方法提交任务时,本质上还是调用的Executor接口的execute方法。
综上所述,在非定时任务类的线程池中提交任务时,本质上都是调用的Executor接口的execute方法。至于调用的是哪个具体实现类的execute方法,我们在后面的文章中深入分析。
五、ScheduledExecutorService接口
5.1 源码
ScheduledExecutorService接口派生自ExecutorService接口,继承了ExecutorService接口的所有功能,并提供了定时处理任务的能力,ScheduledExecutorService接口的源代码比较简单,如下所示。
package java.util.concurrent;
public interface ScheduledExecutorService extends ExecutorService {
//延时delay时间来执行command任务,只执行一次
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
//延时delay时间来执行callable任务,只执行一次
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
//延时initialDelay时间首次执行command任务,之后每隔period时间执行一次
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
//延时initialDelay时间首次执行command任务,之后每延时delay时间执行一次
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
至此,我们分析了线程池体系中重要的顶层接口和抽象类。
5.2 示例
- 方式0:
schedule延时执行Runnable和延时执行Callable执行一次 - 方式1:
scheduleWithFixedDelay周期性的延时执行Runnable接口 上一次任务结束和下一次任务开始之间的时间间隔是固定的=delay - 方式2:
scheduleAtFixedRate周期性的等速率执行Runnable接口 上一次任务开始和下一次任务开始之间的时间间隔是固定的=period - 方式3:
scheduleAtFixedRate周期性的等速率执行Runnable接口 如果任务执行时间大于period,则上一次任务结束之后,立即开始下一次任务;即period=任务执行时间
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
int type = 2;
switch (type) {
case 0:
//延时执行Runnable接口
System.out.println("延时执行Runnable接口 : " + System.currentTimeMillis());
scheduledExecutorService.schedule(() -> {
System.out.println("2秒之后 : " + System.currentTimeMillis());
}, 2000, TimeUnit.MILLISECONDS);
Thread.sleep(2500);
//延时执行Callable接口
System.out.println();
System.out.println("延时执行Callable接口 : " + System.currentTimeMillis());
ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(() -> {
return System.currentTimeMillis();
}, 2, TimeUnit.SECONDS);
System.out.println("2秒之后 :" + scheduledFuture.get());
//等待多长时间
Thread.sleep(1000);
break;
case 1:
//周期性的延时执行
//初始延时,,延时
long initDelay = 5000, delay = 3000;
System.out.println("周期性的延时执行Runnable接口 : " + System.currentTimeMillis());
//周期性的延时执行
scheduledExecutorService.scheduleWithFixedDelay(getRunnable(2000L),
initDelay, delay, TimeUnit.MILLISECONDS);
//等待多长时间
Thread.sleep(20000);
break;
case 2:
//初始延时,执行周期
long initDelay1 = 5000, period = 3000;
System.out.println("周期性的延时执行Runnable接口 : " + System.currentTimeMillis());
//周期性的执行
scheduledExecutorService.scheduleAtFixedRate(getRunnable(2000L),
initDelay1, period, TimeUnit.MILLISECONDS);
//等待多长时间
Thread.sleep(20000);
break;
case 3:
//初始延时, 执行周期
long initDelay2 = 5000, period1 = 2000;
System.out.println("周期性的延时执行Runnable接口 : " + System.currentTimeMillis());
//周期性的执行
scheduledExecutorService.scheduleAtFixedRate(getRunnable(3000L),
initDelay2, period1, TimeUnit.MILLISECONDS);
//等待多长时间
Thread.sleep(20000);
break;
default:
break;
}
//如果没关闭,则关闭
if (!scheduledExecutorService.isShutdown()) {
scheduledExecutorService.shutdown();
}
}
private static Runnable getRunnable(Long millis) {
return () -> {
int number = ThreadLocalRandom.current().nextInt(1000, 3000);
System.out.println("周期性的延时执行Runnable接口 [" + number + "]开始运行 : "
+ System.currentTimeMillis());
//模拟运行
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("周期性的延时执行Runnable接口 [" + number + "]结束运行 : "
+ System.currentTimeMillis());
};
}
}
无论是Runnable还是Callable任务,都只是执行了一次。
type=1时,调用scheduleWithFixedDelay,执行结果:
2018-04-06 16:30:24 INFO - 周期性的延时执行Runnable接口 : 1523003424210
2018-04-06 16:30:29 INFO - 周期性的延时执行Runnable接口 [1513]开始运行 : 1523003429281
2018-04-06 16:30:31 INFO - 周期性的延时执行Runnable接口 [1513]结束运行 : 1523003431281
2018-04-06 16:30:34 INFO - 周期性的延时执行Runnable接口 [2579]开始运行 : 1523003434282
2018-04-06 16:30:36 INFO - 周期性的延时执行Runnable接口 [2579]结束运行 : 1523003436282
2018-04-06 16:30:39 INFO - 周期性的延时执行Runnable接口 [2347]开始运行 : 1523003439283
2018-04-06 16:30:41 INFO - 周期性的延时执行Runnable接口 [2347]结束运行 : 1523003441284
上一次任务结束 与 下一次任务开始 的间隔 = delay = 3秒
type=2时,调用scheduleAtFixedRate,执行结果:
2018-04-06 16:33:48 INFO - 周期性的延时执行Runnable接口 : 1523003628760
2018-04-06 16:33:53 INFO - 周期性的延时执行Runnable接口 [2601]开始运行 : 1523003633808
2018-04-06 16:33:56 INFO - 周期性的延时执行Runnable接口 [2189]开始运行 : 1523003636804
2018-04-06 16:33:59 INFO - 周期性的延时执行Runnable接口 [2071]开始运行 : 1523003639804
2018-04-06 16:34:02 INFO - 周期性的延时执行Runnable接口 [2399]开始运行 : 1523003642803
2018-04-06 16:34:05 INFO - 周期性的延时执行Runnable接口 [1743]开始运行 : 1523003645803
2018-04-06 16:34:08 INFO - 周期性的延时执行Runnable接口 [2351]开始运行 : 1523003648804
上一次任务开始 与 下一次任务开始 的间隔 = delay = 3秒
type=3时,调用scheduleAtFixedRate,执行结果:
2018-04-06 16:35:04 INFO - 周期性的延时执行Runnable接口 : 1523003704193
2018-04-06 16:35:09 INFO - 周期性的延时执行Runnable接口 [1614]开始运行 : 1523003709250
2018-04-06 16:35:12 INFO - 周期性的延时执行Runnable接口 [1614]结束运行 : 1523003712250
2018-04-06 16:35:12 INFO - 周期性的延时执行Runnable接口 [2846]开始运行 : 1523003712250
2018-04-06 16:35:15 INFO - 周期性的延时执行Runnable接口 [2846]结束运行 : 1523003715251
2018-04-06 16:35:15 INFO - 周期性的延时执行Runnable接口 [2760]开始运行 : 1523003715252
2018-04-06 16:35:18 INFO - 周期性的延时执行Runnable接口 [2760]结束运行 : 1523003718253
2018-04-06 16:35:18 INFO - 周期性的延时执行Runnable接口 [2262]开始运行 : 1523003718253
2018-04-06 16:35:21 INFO - 周期性的延时执行Runnable接口 [2262]结束运行 : 1523003721255
2018-04-06 16:35:21 INFO - 周期性的延时执行Runnable接口 [2565]开始运行 : 1523003721255
2018-04-06 16:35:24 INFO - 周期性的延时执行Runnable接口 [2565]结束运行 : 1523003724256
因为任务的执行时间(3秒)大于开始任务的周期period(2秒),所以:
- 上一次任务开始 与 下一次任务开始 的间隔时间 = 任务执行时间
- 上一次任务结束 与 下一次任务开始 的间隔 0 秒
六、总结
以下几个核心组成了Java线程池Executor框架的体系:
Executor接口:是Executor框架的顶级接口,定义了一个用于执行任务的方法execute(Runnable command)。ExecutorService接口:继承自Executor接口,是线程池的主要接口。它扩展了Executor接口,并添加了一些管理线程池的方法,如提交任务、关闭线程池等。AbstractExecutorService抽象类:实现了ExecutorService接口,提供了一些默认的实现,如线程池管理、任务执行等。ThreadPoolExecutor类:是ExecutorService接口的主要实现类,也是Java中最常用的线程池实现类。ThreadPoolExecutor使用核心线程池、任务队列和最大线程池来管理和执行任务。Executors工具类:提供了一些静态工厂方法,用于创建不同类型的线程池,如newFixedThreadPool、newCachedThreadPool等。ScheduledExecutorService接口:继承自ExecutorService接口,具有定时任务调度功能。它可以在指定的时间间隔内周期性地执行任务。Future接口和FutureTask类:用于表示异步计算的结果。可以通过Future对象获取任务的执行结果或取消任务。
它们允许以简单而高效的方式管理和控制并发任务的执行,提高了系统的性能和可伸缩性。在使用线程池时,建议根据具体的应用场景和需求选择合适的线程池类型,并注意正确地管理线程池的生命周期和任务提交。

浙公网安备 33010602011771号