文章目录
-
- [一、FutureTask 的桥梁作用](#一、FutureTask 的桥梁作用)
- [二、Future 模式与异步回调](#二、Future 模式与异步回调)
- [三、 FutureTask获取异步结果的逻辑](#三、 FutureTask获取异步结果的逻辑)
-
- [1. 获取异步执行结果的步骤](#1. 获取异步执行结果的步骤)
- [2. 举例说明](#2. 举例说明)
- [3. FutureTask的异步阻塞问题](#3. FutureTask的异步阻塞问题)
Runnable
用于定义无返回值的任务,而 Callable
用于定义有返回值的任务。然而,Callable
实例无法直接作为 Thread
线程的 target
目标,因为 Thread
的 target
属性类型为 Runnable
,而 Callable
与 Runnable
之间没有任何继承关系。那么,如何将 Callable
与 Thread
结合使用呢?答案是通过 FutureTask
。本文将详细介绍 Callable
、FutureTask
以及它们在异步编程中的应用。
一、FutureTask 的桥梁作用
FutureTask
是 RunnableFuture
接口的实现类,而 RunnableFuture
继承了 Runnable
和 Future
接口。因此,FutureTask
既可以作为 Thread
的 target
,又可以获取异步任务的结果。
如下FutureTask 的核心功能:
java
//实现了Future:获取异步执行结果
//实现了Runnable:可以作为`Thread` 线程的目标任务。
//支持取消任务、查询任务是否完成等功能。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
public class FutureTask<V> implements RunnableFuture<V> {
以下是一个使用 FutureTask
的示例:
java
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class FutureTaskExample {
public static void main(String[] args) throws Exception {
// 创建 Callable 任务
Callable<String> callableTask = () -> {
Thread.sleep(2000); // 模拟耗时操作
return "Task completed!";
};
// 使用 FutureTask 包装 Callable 任务
FutureTask<String> futureTask = new FutureTask<>(callableTask);
// 启动一个新线程执行任务
new Thread(futureTask).start();
System.out.println("Main thread is doing other work...");
// 获取任务结果(会阻塞直到任务完成)
String result = futureTask.get();
System.out.println("Task result: " + result);
}
}
Main thread is doing other work...
Task result: Task completed!
二、Future 模式与异步回调
Future 模式是一种异步编程模式,其核心思想是:
- 异步调用 :任务提交后立即返回一个
Future
对象,表示任务的未来结果。 - 结果获取 :通过
Future
对象获取任务的执行结果,如果任务未完成,调用线程会阻塞直到任务完成。
Future 接口的核心方法
isDone()
:判断任务是否完成。isCancelled()
:判断任务是否被取消。cancel(boolean mayInterruptRunning)
:取消任务的执行。get()
:获取任务结果(阻塞)。get(long timeout, TimeUnit unit)
:在指定时间内获取任务结果(超时抛出异常)。
三、 FutureTask获取异步结果的逻辑
1. 获取异步执行结果的步骤
通过FutureTask类和Callable接口的联合使用可以创建能获取异步执行结果的线程。
创建能够返回结果的线程,启动并获取结果。具体的步骤介绍如下:
- 创建一个Callable接口的实现类,并实现它的call()方法,编写好异步执行的具体逻辑,并且可以有返回值。
- 使用Callable实现类的实例构造一个FutureTask实例。
- 使用FutureTask实例作为Thread构造器的target入参,构造新的Thread线程实例。
- 调用Thread实例的start()方法启动新线程,启动新线程的run()方法并发执行。
- 调用FutureTask对象的get()方法阻塞性地获得并发线程的执行结果。
其内部的执行过程为:启动Thread实例的run()方法并发执行后,会执行FutureTask实例的run()方法,最终会并发执行Callable实现类的call()方法。
2. 举例说明
使用FutureTask实现异步泡茶喝,main线程可以获取烧水线程、清洗线程的执行结果,然后根据结果判断是否具备泡茶条件,如果具备泡茶条件再泡茶。
具体代码见:gitee [并发编程] futureTask demo
几点需要注意:
- FutureTask和Callable都是泛型类,泛型参数表示返回结果的类型。所以,在使用时它们两个实例的泛型参数需要保持一致。
- 通过FutureTask实例的get方法可以获取线程的执行结果,主线程拿到各个线程的结果,然后判断执行。
- 主线程阻塞等待。
3. FutureTask的异步阻塞问题
虽然FutureTask通过 Future
对象管理异步任务的结果,避免阻塞主线程。但通过FutureTask的get()方法获取异步结果时,主线程也会被阻塞。这一点FutureTask和join是一致的,它们都是异步阻塞模式。
异步阻塞的效率往往比较低,被阻塞的主线程不能干任何事情,唯一能干的就是傻傻等待。原生Java API除了阻塞模式的获取结果外,并没有实现非阻塞的异步结果获取方法。
如果需要用到获取的异步结果,得引入一些额外的框架,比如谷歌的Guava框架。