1. 写在前面
FutureTask 是 Java 并发包中的一个重要类,它实现了 RunnableFuture 接口,结合了 Future 和 Runnable 的特性。FutureTask 通常用于异步任务的执行和结果的获取。
如下几个问题,大家看看有没有思考过,在后面阅读源码的过程中都会解答:
- FutureTask 和 Future 有什么区别?
- 如何取消一个 FutureTask?
- FutureTask 是如何实现线程安全的?
- FutureTask 的使用场景有哪些?
- FutureTask 和 CompletableFuture 有什么区别?
- 如何处理 FutureTask 的异常?
- FutureTask 的 run 方法可以多次调用吗?
2. 全局视角
2.1 Runnable 接口
Runnable 接口是一个函数式接口,定义了一个 run 方法,用于包含需要在新线程中执行的代码。
@FunctionalInterface
public interface Runnable {
void run();
}
2.2 Future 接口
Future 接口表示一个异步计算的结果,并提供了检查任务状态、获取结果、取消任务等方法。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
2.3 RunnableFuture 接口
RunnableFuture 接口继承了 Runnable 和 Future 接口,表示一个既可以作为任务执行(Runnable),又可以获取结果(Future)的任务。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
2.4 FutureTask 的关键方法
FutureTask 实现了 RunnableFuture 接口中的所有方法,并提供了一些额外的方法和机制来管理任务的执行和状态。
2.4.1 构造方法
- FutureTask(Callable callable):通过 Callable 创建 FutureTask。
- FutureTask(Runnable runnable, V result):通过 Runnable 创建 FutureTask,并指定任务完成后的结果。
2.4.2 任务执行
void run():执行任务,如果任务已完成或已取消则不执行。
2.4.3 结果获取
- V get():获取任务的结果,如果任务未完成则阻塞。
- V get(long timeout, TimeUnit unit):在指定时间内获取任务的结果。
2.4.4. 任务状态
- boolean isDone():检查任务是否已完成。
- boolean isCancelled():检查任务是否已取消。
- boolean cancel(boolean mayInterruptIfRunning):取消任务。
3. 从使用说起
3.1 使用 FutureTask 执行 Callable 任务
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskExample {
public static void main(String[] args) {
// 创建一个 Callable 任务
Callable<String> callableTask = () -> {
Thread.sleep(1000); // 模拟耗时操作
return "Task completed";
};
// 创建一个 FutureTask
FutureTask<String> futureTask = new FutureTask<>(callableTask);
// 提交给 Executor 执行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(futureTask);
try {
// 获取结果(阻塞直到任务完成)
String result = futureTask.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
- 创建一个 Callable 任务,模拟一个耗时操作。
- 使用 Callable 创建一个 FutureTask 实例。
- 将 FutureTask 提交给 ExecutorService 执行。
- 通过 FutureTask 的 get 方法获取任务的结果,该方法会阻塞直到任务完成。
- 最后关闭 ExecutorService。
3.2 使用 FutureTask 执行 Runnable 任务并获取结果
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskRunnableExample {
public static void main(String[] args) {
// 创建一个 Runnable 任务
Runnable runnableTask = () -> {
try {
Thread.sleep(1000); // 模拟耗时操作
System.out.println("Runnable task completed");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 使用 Runnable 和结果值创建一个 FutureTask
FutureTask<String> futureTask = new FutureTask<>(runnableTask, "Task finished");
// 提交给 Executor 执行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(futureTask);
try {
// 获取结果(阻塞直到任务完成)
String result = futureTask.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
- 创建一个 Runnable 任务,模拟一个耗时操作。
- 使用 Runnable 和任务完成后的结果值创建一个 FutureTask 实例。
- 将 FutureTask 提交给 ExecutorService 执行。
- 通过 FutureTask 的 get 方法获取任务的结果,该方法会阻塞直到任务完成。
- 最后关闭 ExecutorService。
3.3 取消 FutureTask
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskCancelExample {
public static void main(String[] args) {
// 创建一个 Callable 任务
Callable<String> callableTask = () -> {
Thread.sleep(3000); // 模拟耗时操作
return "Task completed";
};
// 创建一个 FutureTask
FutureTask<String> futureTask = new FutureTask<>(callableTask);
// 提交给 Executor 执行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(futureTask);
try {
// 等待一段时间后取消任务
Thread.sleep(1000);
boolean cancelled = futureTask.cancel(true);
System.out.println("Task cancelled: " + cancelled);
// 获取任务状态
if (!futureTask.isCancelled()) {
String result = futureTask.get();
System.out.println("Result: " + result);
} else {
System.out.println("Task was cancelled.");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
- 创建一个 Callable 任务,模拟一个耗时操作。
- 使用 Callable 创建一个 FutureTask 实例。
- 将 FutureTask 提交给 ExecutorService 执行。
- 等待一段时间后,通过 FutureTask 的 cancel 方法取消任务。
- 检查任务是否被取消,如果未取消则获取任务结果,否则输出任务已取消的信息。
- 最后关闭 ExecutorService。
4. FutureTask 和 Future 有什么区别?
- Future 是一个接口,表示一个异步计算的结果。
- FutureTask 是 Future 的一个具体实现,结合了 Future 和 Runnable 的特性,可以被提交给 Executor 执行,也可以直接在线程中执行。
5. 如何取消一个 FutureTask?
可以调用 FutureTask 的 cancel 方法来取消任务:
futureTask.cancel(true); // 取消任务,true 表示如果任务正在运行,可以中断它
6. FutureTask 是如何实现线程安全的?
FutureTask 使用了内部状态和同步机制来确保线程安全。例如,它使用 CAS 操作和 volatile 变量来管理任务的状态(如未开始、正在运行、已完成、已取消等)。
7. FutureTask 的使用场景有哪些?
- 异步计算:可以在后台执行耗时任务,并在需要时获取结果。
- 并行处理:可以将多个任务并行执行,并在所有任务完成后处理结果。
- 延迟计算:可以延迟执行任务,直到需要结果时再执行。
8. FutureTask 和 CompletableFuture 有什么区别?
- FutureTask 是 Java 5 引入的,用于表示一个可取消的异步任务。
- CompletableFuture 是 Java 8 引入的,提供了更丰富的功能,如链式调用、组合多个异步任务、处理异步任务的结果等。
9. 如何处理 FutureTask 的异常?
可以在获取结果时处理异常:
try {
String result = futureTask.get();
} catch (InterruptedException e) {
// 处理线程中断异常
} catch (ExecutionException e) {
// 处理任务执行异常
Throwable cause = e.getCause(); // 获取实际异常原因
}
10. FutureTask 的 run 方法可以多次调用吗?
FutureTask 的 run 方法只能被调用一次。多次调用 run 方法不会重新执行任务,只会返回相同的结果或状态。
系列文章
7.jdk源码阅读之ConcurrentHashMap(上)
8.jdk源码阅读之ConcurrentHashMap(下)