深入解析 Java Future 类及代码示例

在 Java 并发编程中,Future 类是一个用于表示异步任务计算结果的接口。它为我们提供了获取异步执行任务的结果、检查任务状态、取消任务等功能。在实际开发中,Future 类常与 ExecutorService 配合使用,来管理并发任务的执行。本文将深入解析 Future 类的功能,并通过具体的代码示例帮助理解其用法。


Future 接口概述

Future 接口定义了与异步任务相关的多个方法,它用于获取任务执行结果、检查任务状态、取消任务等操作。

public interface Future<V> {
    V get() throws InterruptedException, ExecutionException;  // 阻塞获取结果
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;  // 带超时的阻塞获取结果
    boolean cancel(boolean mayInterruptIfRunning);  // 取消任务
    boolean isCancelled();  // 判断任务是否被取消
    boolean isDone();  // 判断任务是否已完成
}

Future 方法详解

1. get() 方法

get() 是一个阻塞方法,用于获取异步任务的执行结果。如果任务已经完成,get() 会立即返回结果;如果任务还在执行,调用线程会被阻塞,直到任务完成并返回结果。该方法会抛出两个异常:

  • InterruptedException:当当前线程在等待任务时被中断。
  • ExecutionException:当任务执行发生异常时,封装在 ExecutionException 中。
V result = future.get();  // 阻塞获取任务结果

2. get(long timeout, TimeUnit unit) 方法

与 get() 类似,但是带有超时限制。如果任务在指定时间内没有完成,会抛出 TimeoutException。如果任务超时,get() 方法会立即返回,且当前线程不再阻塞。

V result = future.get(5, TimeUnit.SECONDS);  // 等待最多 5 秒

3. cancel(boolean mayInterruptIfRunning) 方法

该方法用于取消正在执行的任务。mayInterruptIfRunning 参数指定是否可以中断正在执行的任务:

  • 如果为 true,则可以中断任务。
  • 如果为 false,则任务在执行过程中无法被中断。

该方法返回一个布尔值:

  • 如果任务已经完成或无法取消,返回 false
  • 如果任务成功取消,返回 true
boolean cancelled = future.cancel(true);  // 取消任务,并尝试中断

4. isCancelled() 方法

判断任务是否已被取消。如果任务已经开始执行并且无法取消,返回 false;如果任务尚未执行且已经取消,返回 true

boolean isCancelled = future.isCancelled();  // 判断任务是否被取消

5. isDone() 方法

判断任务是否已经完成。无论任务是正常完成,还是由于异常或被取消,都会返回 true。如果任务正在执行或尚未开始,返回 false

boolean isDone = future.isDone();  // 判断任务是否已完成

Future 类与 ExecutorService 的结合使用

Future 接口常与 ExecutorService 接口结合使用,ExecutorService 提供了多种方法来提交异步任务,其中最常用的 submit() 方法返回一个 Future 对象,允许获取任务的执行结果、判断任务的状态以及取消任务。

代码示例:ExecutorService 和 Future 基本用法

import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建一个固定线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 提交一个异步任务,返回 Future 对象
        Future<Integer> future = executorService.submit(() -> {
            System.out.println("Task started");
            Thread.sleep(2000);  // 模拟耗时任务
            return 123;
        });

        // 等待任务执行完成,并获取结果
        Integer result = future.get();  // 阻塞获取结果
        System.out.println("Task result: " + result);  // 输出任务的结果

        executorService.shutdown();  // 关闭线程池
    }
}

在这个例子中:

  • 我们创建了一个固定大小为 2 的线程池。
  • 提交了一个异步任务并返回 Future 对象。
  • 使用 future.get() 方法等待任务完成并获取结果。

代码示例:带超时限制的 Future.get()

import java.util.concurrent.*;

public class TimeoutFutureExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 提交一个耗时任务
        Future<Integer> future = executorService.submit(() -> {
            Thread.sleep(3000);  // 模拟一个耗时任务
            return 123;
        });

        try {
            // 获取结果,但最多等待 2 秒
            Integer result = future.get(2, TimeUnit.SECONDS);
            System.out.println("Task result: " + result);
        } catch (TimeoutException e) {
            System.out.println("Task timed out");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        executorService.shutdown();
    }
}

在这个例子中:

  • 任务会阻塞 3 秒,但 get(2, TimeUnit.SECONDS) 方法会等待最多 2 秒。
  • 如果任务没有在 2 秒内完成,会抛出 TimeoutException

代码示例:取消任务

import java.util.concurrent.*;

public class CancelFutureExample {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 提交一个耗时任务
        Future<Integer> future = executorService.submit(() -> {
            Thread.sleep(5000);  // 模拟耗时任务
            return 123;
        });

        // 1 秒后取消任务
        Thread.sleep(1000);
        boolean cancelled = future.cancel(true);  // 尝试中断任务
        System.out.println("Task cancelled: " + cancelled);

        executorService.shutdown();
    }
}

在这个例子中:

  • 提交一个需要 5 秒钟的任务。
  • 等待 1 秒钟后取消任务。
  • cancel(true) 尝试中断正在运行的任务。

Future 与 Callable 类结合使用

Callable 接口与 Runnable 相似,都是可以在新线程中执行的任务,但 Callable 允许任务返回结果,并且可以抛出异常。ExecutorService.submit() 方法返回一个 Future 对象,Future 对象可用于获取 Callable 任务的结果。

代码示例:使用 Callable 和 Future 获取任务结果

import java.util.concurrent.*;

public class CallableFutureExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 使用 Callable 创建任务
        Callable<Integer> task = () -> {
            Thread.sleep(2000);  // 模拟耗时任务
            return 42;
        };

        // 提交任务并返回 Future 对象
        Future<Integer> future = executorService.submit(task);

        // 获取任务结果
        Integer result = future.get();  // 阻塞直到任务完成
        System.out.println("Task result: " + result);  // 输出 42

        executorService.shutdown();
    }
}

在这个例子中:

  • 使用 Callable 创建任务,任务执行完成后返回一个整数结果。
  • 使用 Future.get() 获取结果。

总结

  1. Future 类是 Java 并发编程中非常重要的接口,用于表示异步计算的结果。
  2. Future 提供了多种方法来操作异步任务,如获取结果 (get())、设置超时 (get(long, TimeUnit))、取消任务 (cancel())、检查任务状态 (isDone() 和 isCancelled())。
  3. Future 常与 ExecutorService 一起使用,通过 submit() 方法提交任务,返回 Future 对象以获取任务结果。
  4. Future 可以与 Callable 结合使用,支持任务返回结果并抛出异常。
  5. 在实际应用中,Future 可以用于处理任务的异步执行、管理任务状态、控制任务超时等操作,尤其适合并发编程和多线程任务管理。

通过这些代码示例,你可以更好地理解和运用 Java 的 Future 类来处理并发任务。