一、概述
JDK中提供了Executor框架,便于我们进行多线程操作。
关联文章:
二、Executor 框架的两层调度模型
说明:
从图中可以看出,Java中执行异步任务采用两级调度模型来完成。
在上层,应用程序通过Executor框架控制上层的调度;
在下层,调度由操作系统内核控制,下层的调度不受应用程序的控制。
二、Executor 框架的类图
上图是 Executor框架 提供的功能。
Executor接口: 它是Executor框架的基础,它将任务的提交与任务的执行分离开来,无返回值;
ExecutorService接口: 提供了 submit(Runnable)、submit(Callable) 两个方法,带返回值;
AbstractExecutorService: 提供了两个转换方法,对 submit() 传递的参数进行包装;
ThreadPoolExecutor: 线程池的核心类,根据参数设置的不同,可以创建出多种类型的线程池;
ScheduledExecutorService接口: 由于 ThreadPoolExecutor 执行的任务都是实时的,不具备延迟功能,因此扩展了ExecutorService接口,提供了执行任务的功能;
ScheduledThreadPoolExecutor: ScheduledExecutorService接口的具体实现类;
Executors: 一个 Executor工厂类,提供了 FixedThreadPool
、SingleThreadExecutor
、CachedThreadPool
、ScheduledThreadPoolExecutor
等类型的线程池;
三、Executor 框架包含的成员
Executor框架主要由3大部分组成。
-
任务。 包括被执行任务需要实现的接口:Runnable接口 或 Callable接口。
-
任务的执行。 包括任务执行机制的核心接口
Executor
,以及继承自Executor的ExecutorService
接口。Executor框架提供了两个实现了ExecutorService接口的实现类:
ThreadPoolExecutor
、ScheduledThreadPoolExecutor
。 -
异步计算的结果。 包括接口
Future
和实现Future接口的FutureTask
类。
四、小结
-
提交任务到线程池的方式有两种:
execute()
、submit()
execute()
:不带返回值;submit()
:带返回值Future
;
-
提交到线程池的 task 有两种类型:
Runnable
、Callable
Runnable
:不带返回值的 task,execute(Runnable)
和submit(Runnable)
;Callable
:带返回值的 task,submit(Callable)
;
-
线程池只能执行 Runnable 类型的 task,这个可以由
Executor.execute(Runnable)
看出;public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); //RunnableFuture接口实现了Runnable和Future接口 RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); //submit(Runnable)方法最终还是调用execute(Runnable) return ftask; } public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); //submit(Callable)方法最终还是调用execute(Runnable) return ftask; }
-
RunnableFuture
、FutureTask
的介绍。RunnableFuture
接口:继承了Runnable 接口
和Future 接口
。FutureTask
类:实现了RunnableFuture 接口
。
FutureTask 内部只保存Callable
类型的变量,但是FutureTask构造接收Runnable
、Callable
两种类型的参数。因此,当接收到Runnable
类型的参数时,需要调用Executors.callable(runnable, result)
方法进行转换 。
// RunnableFuture.class // 具备了提交给线程池执行的能力(Runnable),也具备取出返回值的能力(Future) public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); } // FutureTask.class public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } // FutureTask.class public FutureTask(Runnable runnable, V result) { //由于FutureTask只接收Callable,所以这里需要做一下转换。 this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable } // Executor.class public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); //RunnableAdapter实现了Callable接口。 }
-
由第1点、第2点和第3点可以得出以下结论:
- 如果是由
execute(Runnable)
提交的 task(Runnable类型),则线程池可以直接执行。 - 如果是由
submit(Runnable)
提交的 task(Runnable类型),由于需要返回一个Future
对象接收数据,因此线程池不能直接执行 task,需要对 Runnable类型的 task 进行包装 。可以参考第4点。
// AbstractExecutorService.class public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); //RunnableFuture接口实现了Runnable和Future接口 execute(ftask); //submit(Runnable)方法最终还是调用execute(Runnable) return ftask; //返回的RunnableFuture实现了Future接口 } // AbstractExecutorService.class protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); //可以参考第4点 }
- 如果是由
submit(Callable)
提交的 task(Callable类型),由于线程池只能执行 Runnable 类型的 task,且需要返回一个Future
对象接收数据,因此线程池不能直接执行 task,需要对 Callable 类型的 task 进行包装 。//可以参考第4点。
// AbstractExecutorService.class public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); //RunnableFuture接口实现了Runnable和Future接口 execute(ftask); //submit(Callable)方法最终还是调用execute(Runnable) return ftask; //返回的RunnableFuture实现了Future接口 } // AbstractExecutorService.class protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); //可以参考第4点 }
- 如果是由
-
通过返回的
Future
进行取值。由submit()
提交任务后会返回一个Future
,所以我们可以通过Future.get()
取值(线程阻塞)。