第一部分:Callable基础

理解Callable接口与Runnable的区别

学习目标

掌握Callable接口的特点、使用场景以及与Runnable的区别

Runnable接口

Java早期线程执行接口

  • void run()方法
  • 无返回值
  • 不能抛出受检异常
  • 适合简单异步任务
public class MyRunnable implements Runnable { @Override public void run() { // 执行任务,无返回值 System.out.println("Runnable任务执行"); } }

Callable接口

Java 5引入的增强接口

  • V call()方法
  • 有返回值
  • 可抛出异常
  • 适合复杂异步任务
import java.util.concurrent.Callable; public class MyCallable implements Callable { @Override public String call() throws Exception { // 执行任务并返回结果 return "Callable任务结果"; } }
特性 Runnable Callable
返回值
异常处理 不能抛出受检异常 可抛出受检异常
接口方法 run() call()
使用场景 简单异步任务 需要返回结果的异步任务
线程池提交 execute() submit()

使用建议

1. 需要任务结果时使用Callable
2. 需要处理任务异常时使用Callable
3. 简单任务使用Runnable
4. 使用ExecutorService.submit()提交Callable任务

第二部分:Future使用

掌握Future接口获取异步任务结果

学习目标

理解Future接口的核心方法及使用方式

isDone()

检查任务是否完成

返回值: true-任务完成,false-任务未完成

cancel()

尝试取消任务

参数: mayInterruptIfRunning - 是否中断运行中的任务

isCancelled()

检查任务是否被取消

返回值: true-任务已取消,false-任务未取消

get()

获取任务结果(阻塞)

返回值: 任务执行结果

get(long, TimeUnit)

获取任务结果(带超时)

参数: timeout - 超时时间,unit - 时间单位

Future基本使用示例

import java.util.concurrent.*; public class FutureExample { public static void main(String[] args) throws Exception { // 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(3); // 提交Callable任务 Future future = executor.submit(() -> { Thread.sleep(2000); // 模拟耗时操作 return "任务完成结果"; }); System.out.println("任务已提交,主线程继续执行其他操作..."); // 检查任务是否完成 if (!future.isDone()) { System.out.println("任务仍在执行中..."); } // 获取任务结果(阻塞) String result = future.get(); System.out.println("任务结果: " + result); // 关闭线程池 executor.shutdown(); } }
任务已提交,主线程继续执行其他操作...
任务仍在执行中...
任务结果: 任务完成结果

第三部分:FutureTask详解

掌握FutureTask的使用与原理

学习目标

理解FutureTask的实现原理及使用场景

FutureTask原理

FutureTask是Future接口的实现类

  • 实现RunnableFuture接口
  • 可包装Callable或Runnable
  • 内部使用状态机管理任务状态
  • 使用AQS实现同步控制

状态转换

FutureTask的状态变化:

  • NEW:新建状态
  • COMPLETING:执行中
  • NORMAL:正常完成
  • EXCEPTIONAL:异常完成
  • CANCELLED:已取消
  • INTERRUPTING:中断中
  • INTERRUPTED:已中断
NEW
COMPLETING
NORMAL
EXCEPTIONAL
CANCELLED

FutureTask使用示例

import java.util.concurrent.*; public class FutureTaskExample { public static void main(String[] args) throws Exception { // 创建Callable任务 Callable task = () -> { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; Thread.sleep(10); // 模拟耗时 } return sum; }; // 创建FutureTask FutureTask futureTask = new FutureTask<>(task); // 创建线程执行FutureTask Thread thread = new Thread(futureTask); thread.start(); // 主线程执行其他操作 System.out.println("主线程执行其他任务..."); // 获取任务结果 Integer result = futureTask.get(); System.out.println("计算结果: " + result); } }

使用场景

1. 需要手动启动任务时
2. 需要将任务提交给Thread执行时
3. 需要将任务作为参数传递时
4. 需要组合多个Future时

第四部分:异常处理

正确处理Callable任务中的异常

学习目标

掌握Callable任务中异常的处理方式

异常处理示例

import java.util.concurrent.*; public class ExceptionHandlingExample { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(() -> { // 模拟可能抛出异常的操作 if (Math.random() > 0.5) { throw new RuntimeException("任务执行异常"); } return "任务成功完成"; }); try { String result = future.get(); System.out.println("任务结果: " + result); } catch (InterruptedException e) { System.out.println("任务被中断"); Thread.currentThread().interrupt(); } catch (ExecutionException e) { System.out.println("任务执行异常: " + e.getCause().getMessage()); } finally { executor.shutdown(); } } }
任务执行异常: 任务执行异常

重要注意事项

1. Callable任务中抛出的异常会被封装在ExecutionException中
2. 调用Future.get()时捕获ExecutionException
3. 通过e.getCause()获取原始异常
4. 正确处理InterruptedException(恢复中断状态)

第五部分:超时控制

防止任务长时间阻塞

学习目标

掌握使用get(timeout, unit)方法实现超时控制

超时控制示例

import java.util.concurrent.*; public class TimeoutExample { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(() -> { Thread.sleep(3000); // 模拟长时间任务 return "任务完成"; }); try { // 设置超时时间为2秒 String result = future.get(2, TimeUnit.SECONDS); System.out.println("任务结果: " + result); } catch (TimeoutException e) { System.out.println("任务超时,取消任务"); future.cancel(true); // 尝试中断任务 } catch (InterruptedException e) { System.out.println("任务被中断"); Thread.currentThread().interrupt(); } catch (ExecutionException e) { System.out.println("任务执行异常: " + e.getCause().getMessage()); } finally { executor.shutdown(); } } }
任务超时,取消任务

超时处理策略

1. 使用get(timeout, unit)设置超时时间
2. 捕获TimeoutException处理超时
3. 调用future.cancel(true)尝试取消任务
4. 记录超时日志或通知监控系统
5. 考虑重试机制或降级处理

第六部分:常见错误与陷阱

避免Callable与Future使用中的常见问题

1. 忘记调用get()方法

未获取结果导致异常未被抛出

// 错误示例:忘记调用get() Future future = executor.submit(() -> { throw new RuntimeException("任务异常"); }); // 忘记调用future.get(),异常未被捕获

正确做法:始终调用get()方法并处理异常

2. 忽略InterruptedException

未正确处理中断异常

// 错误示例:忽略InterruptedException try { future.get(); } catch (InterruptedException e) { // 未处理中断状态 e.printStackTrace(); }

正确做法:恢复中断状态 Thread.currentThread().interrupt()

3. 未处理ExecutionException

任务异常未被正确处理

// 错误示例:未处理ExecutionException try { String result = future.get(); } catch (InterruptedException e) { // 处理中断 } // 缺少ExecutionException处理

正确做法:捕获ExecutionException并处理原始异常

4. 过度阻塞

未设置超时导致主线程长时间阻塞

// 错误示例:未设置超时 Future future = executor.submit(longRunningTask); String result = future.get(); // 可能长时间阻塞

正确做法:使用get(timeout, unit)设置超时

5. 任务取消后调用get()

导致CancellationException

// 错误示例:取消后调用get() future.cancel(true); String result = future.get(); // 抛出CancellationException

正确做法:检查isCancelled()后再调用get()

6. 线程池未关闭

导致线程泄漏

// 错误示例:忘记关闭线程池 ExecutorService executor = Executors.newFixedThreadPool(5); Future future = executor.submit(task); // 使用后未关闭

正确做法:使用try-finally或try-with-resources关闭线程池

第七部分:CompletableFuture

Java 8引入的增强异步编程API

学习目标

掌握CompletableFuture的基本使用和组合操作

CompletableFuture优势

  • 支持链式调用
  • 支持异步回调
  • 支持任务组合
  • 支持异常处理
  • 支持手动完成

与Future对比

  • Future:阻塞获取结果
  • CompletableFuture:非阻塞回调
  • Future:不支持组合
  • CompletableFuture:支持复杂组合
  • Future:异常处理复杂
  • CompletableFuture:内置异常处理

CompletableFuture基本使用

import java.util.concurrent.*; public class CompletableFutureExample { public static void main(String[] args) throws Exception { // 异步执行任务 CompletableFuture future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "Hello"; }); // 任务完成后处理结果 future.thenAccept(result -> System.out.println(result + " World")); // 等待任务完成 future.get(); } }

任务组合示例

import java.util.concurrent.*; public class CompletableFutureCombine { public static void main(String[] args) throws Exception { CompletableFuture future1 = CompletableFuture.supplyAsync(() -> "Hello"); CompletableFuture future2 = CompletableFuture.supplyAsync(() -> "World"); // 组合两个任务结果 CompletableFuture combined = future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2); // 处理最终结果 combined.thenAccept(System.out::println); // 等待任务完成 combined.get(); } }

使用建议

1. 简单任务使用Future
2. 复杂异步流程使用CompletableFuture
3. 使用supplyAsync()替代Callable
4. 使用thenApply()转换结果
5. 使用thenCompose()组合多个CompletableFuture

第八部分:练习与测验

通过练习巩固Callable与Future知识

动手练习

  • 使用Callable实现并行计算任务
  • 使用Future实现任务超时控制
  • 使用CompletableFuture实现异步任务链
  • 实现带异常处理的Callable任务
1

Callable与Future知识测验

以下关于Callable和Future的描述,哪项是错误的?
以下哪种情况最适合使用Future.get(long timeout, TimeUnit unit)?

学习资源推荐

1. 《Java并发编程实战》第6章
2. Oracle官方文档:java.util.concurrent
3. FutureTask源码分析
4. CompletableFuture高级用法