在Java并发编程中,仅会使用线程池是远远不够的。为了充分发挥多线程的威力,我们还需要深入理解Callable、Future和FutureTask的内在机制,并且能熟练地手写一个功能完备的线程池。让我们一同攻克这座高峰,领略并发编程的无穷魅力!
一、Callable和Future: 异步编程的钥匙
在传统的多线程编程中,我们仅能通过Runnable提交没有返回值的简单任务。而Callable则赋予了我们创建"有返回值的任务"的能力,为异步编程的大门打开了一扇窗。
Future则是Callable的完美绑定,通过它我们不仅能拿到任务的执行结果,还能判断任务的执行状态,甚至是取消任务。随着异步编程日益普及,掌握这对搭档就变得尤为重要。
Callable 接口和 Future 接口经常一起使用来执行异步任务。
Callable 接口与 Runnable 接口类似,但它可以返回结果并且可以抛出异常。
Future 接口代表异步计算的结果,并且可以用来检查计算是否完成,取消计算,以及获取计算的结果。
以下是一个使用 Callable 和 Future 完成异步排序任务的示例代码,其中包含异步取消、异常处理和超时控制:
import java.util.Arrays;
import java.util.concurrent.*;
public class AsyncSortExample {
public static void main(String[] args) {
// 创建一个线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
// 创建一个 Callable 任务,执行排序操作
Callable<Integer[]> callable = () -> {
try {
// 模拟耗时的排序任务
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 假设我们对数组进行排序
Integer[] data = {5, 3, 8, 1, 2};
Arrays.sort(data);
return data;
};
// 提交任务并获取 Future 对象
Future<Integer[]> future = executor.submit(callable);
// 模拟其他任务或操作...
// ...
// 超时控制,等待结果,设置超时时间为 3 秒
try {
Integer[] sortedData = future.get(3, TimeUnit.SECONDS);
System.out.println("Sorted data: " + Arrays.toString(sortedData));
} catch (TimeoutException e) {
System.out.println("Task timed out, attempting to cancel...");
// 超时后尝试取消任务
future.cancel(true);
} catch (CancellationException e) {
System.out.println("Task was cancelled.");
} catch (ExecutionException e) {
// 处理 Callable 任务中的异常
System.out.println("Task threw an exception: " + e.getCause().getMessage());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
executor.shutdown();
}
}
}
代码解释:
- 线程池创建:使用
Executors.newSingleThreadExecutor()创建了一个单线程的线程池。 - Callable 任务定义:定义了一个
Callable任务,该任务模拟了一个耗时的排序操作。 - 任务提交:通过
executor.submit(callable)提交任务,并获得一个Future对象。 - 超时控制:使用
future.get(3, TimeUnit.SECONDS)尝试获取结果,设置了 3 秒的超时时间。 - 异常处理:
TimeoutException:如果任务执行超过 3 秒,将捕获此异常,并尝试取消任务。CancellationException:如果任务已经被取消,将捕获此异常。ExecutionException:如果 Callable 任务中抛出了异常,将捕获ExecutionException并通过e.getCause()获取原始异常。
- 取消任务:如果需要取消任务,可以使用
future.cancel(true),其中true参数表示中断正在执行的任务。 - 关闭线程池:在
finally块中关闭线程池,释放资源。
二、FutureTask源码剖析
FutureTask的出现,为Callable和Future之间架起了一座桥梁。作为两者的融合体,它不仅可以扮演Callable执行的载体角色,自身也是一个Future实例。

786

被折叠的 条评论
为什么被折叠?



