目录
一、ThreadPoolExecutor的中的submit和FutureTask
二、通过Executors 创建线程池的一些实例(Callable和Runnable的在其中的体现)
前言:
ThreadPoolExecutor 的 void execute(Runnable command) 方法,利用这个方法虽然可以提交任务,但是却没有办法获取任务的执行结果(execute() 方法没有返回值)
而很多场景下,我们又都是需要获取任务的执行结果的。那 ThreadPoolExecutor 是否提供了相关功能呢?必须的,这么重要的功能当然需要提供了——那就是sumbit
- execute只能提交Runnable类型的任务,无返回值。submit既可以提交Runnable类型的任务,也可以提交Callable类型的任务,会有一个类型为Future的返回值,但当任务类型为Runnable时,返回值为null。
- execute在执行任务时,如果遇到异常会直接抛出,而submit不会直接抛出,只有在使用Future的get方法获取返回值时,才会抛出异常。
一、ThreadPoolExecutor的中的submit和FutureTask
Executors 本质上是 ThreadPoolExecutor 类的封装.
Executors类和ThreadPoolExecutor都是util.concurrent并发包下面的类, Executos下面的newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor、newCachedThreadPool底线的实现都是用的ThreadPoolExecutor实现的,所有ThreadPoolExecutor更加灵活。
Java 通过 ThreadPoolExecutor 提供的 3 个 submit() 方法和 1 个 FutureTask 工具类来支持获得任务执行结果的需求。下面我们先来介绍这 3 个 submit() 方法,这 3 个方法的方法签名如下。
// 提交 Runnable 任务
Future<?>
submit(Runnable task);
// 提交 Callable 任务
<T> Future<T>
submit(Callable<T> task);
// 提文 Runnable 任务及结果引用
<T> Future<T>
submit(Runnable task T result);
你会发现它们的返回值都是 Future 接口
Future 接口有 5 个方法
取消任务的方法 cancel()、判断任务是否已取消的方法 isCancelled()、判断任务是否已结束的方法 isDone()以及2 个获得任务执行结果的 get() 和 get(timeout, unit)
其中最后一个 get(timeout, unit) 支持超时机制。通过 Future 接口的这 5 个方法你会发现,我们提交的任务不但能够获取任务执行结果,还可以取消任务。不过需要注意的是:这两个 get() 方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用 get() 方法的线程会阻塞,直到任务执行完才会被唤醒。
这 3 个 submit() 方法之间的区别在于方法参数不同,下面我们简要介绍一下。
- 提交 Runnable 任务
submit(Runnable task)
:这个方法的参数是一个 Runnable 接口,Runnable 接口的 run() 方法是没有返回值的,所以submit(Runnable task)
这个方法返回的 Future 仅可以用来断言任务已经结束了,类似于 Thread.join()。- 提交 Callable 任务
submit(Callable<T> task)
:这个方法的参数是一个 Callable 接口,它只有一个 call() 方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get() 方法来获取任务的执行结果。- 提交 Runnable 任务及结果引用
submit(Runnable task, T result)
:这个方法很有意思,假设这个方法返回的 Future 对象是 f,f.get() 的返回值就是传给 submit() 方法的参数 result。这个方法该怎么用呢?下面这段示例代码展示了它的经典用法。需要你注意的是 Runnable 接口的实现类 Task 声明了一个有参构造函数Task(Result r)
,创建 Task 对象的时候传入了 result 对象,这样就能在类 Task 的 run() 方法中对 result 进行各种操作了。result 相当于主线程和子线程之间的桥梁,通过它主子线程可以共享数据。
那么既然Executor是对ThreadPoolExecutor的封装,那么通过Executor创建的线程池自然也同样有上述3个submit方法和1个FutureTask工具类。