1. 概述

本教程将深入探讨Java并发编程中的Future接口。这个自Java 1.5起就存在的接口,在处理异步调用和并发任务时非常实用。对于需要长时间运行的操作,Future能让我们优雅地管理异步任务的结果。

2. 创建Future实例

简单来说,Future代表一个异步计算的未来结果。当任务完成后,结果会出现在Future对象中。下面我们看看如何创建返回Future实例的方法。

2.1 使用FutureTask实现Future

我们创建一个简单的整数平方计算类。虽然这个计算本身很快,但为了模拟耗时操作,我们添加了1秒的延迟:

public class SquareCalculator {    
    
    private ExecutorService executor 
      = Executors.newSingleThreadExecutor();
    
    public Future<Integer> calculate(Integer input) {        
        return executor.submit(() -> {
            Thread.sleep(1000);
            return input * input;
        });
    }
}

实际计算逻辑封装在call()方法中(这里用lambda表达式实现)。关键点在于CallableExecutorService的使用:

  • Callable:表示返回结果的任务接口,只有一个call()方法
  • ExecutorService:负责在新线程中启动任务并返回Future对象

通过Executors.newSingleThreadExecutor()获取单线程执行器,调用submit()方法提交任务后,会返回一个FutureTask实例(Future接口的实现类)。

3. 使用Future

现在我们学习如何操作Future实例,重点掌握其核心API方法。

3.1 使用isDone()和get()获取结果

调用calculate()方法后,通过以下两个方法获取结果:

  • Future.isDone():检查任务是否完成
  • Future.get():获取实际结果(会阻塞直到任务完成)
Future<Integer> future = new SquareCalculator().calculate(10);

while(!future.isDone()) {
    System.out.println("Calculating...");
    Thread.sleep(300);
}

Integer result = future.get();

⚠️ 注意:get()方法会阻塞线程。在实际使用中,应先检查isDone()再调用get(),避免无限等待。

get()还有带超时的重载版本:

Integer result = future.get(500, TimeUnit.MILLISECONDS);

如果超时未完成,会抛出TimeoutException

3.2 使用cancel()取消Future

当不再需要任务结果时,可以取消任务:

Future<Integer> future = new SquareCalculator().calculate(4);

boolean canceled = future.cancel(true);

✅ 取消成功后:

  • 后续调用get()会抛出CancellationException
  • 可通过isCancelled()检查任务是否已取消

❌ 取消失败时返回false。参数true表示尝试中断执行线程。

4. 线程池进阶使用

当前示例使用单线程执行器(newSingleThreadExecutor)。让我们测试并发场景:

SquareCalculator squareCalculator = new SquareCalculator();

Future<Integer> future1 = squareCalculator.calculate(10);
Future<Integer> future2 = squareCalculator.calculate(100);

while (!(future1.isDone() && future2.isDone())) {
    System.out.println(
      String.format(
        "future1 is %s and future2 is %s", 
        future1.isDone() ? "done" : "not done", 
        future2.isDone() ? "done" : "not done"
      )
    );
    Thread.sleep(300);
}

Integer result1 = future1.get();
Integer result2 = future2.get();

System.out.println(result1 + " and " + result2);

squareCalculator.shutdown();

输出显示任务串行执行(总耗时约2秒):

calculating square for: 10
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
calculating square for: 100
future1 is done and future2 is not done
future1 is done and future2 is not done
future1 is done and future2 is not done
100 and 10000

改为使用固定大小线程池实现真正并行:

public class SquareCalculator {
 
    private ExecutorService executor = Executors.newFixedThreadPool(2);
    
    //...
}

现在任务并行执行(总耗时约1秒):

calculating square for: 10
calculating square for: 100
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
100 and 10000

其他常用线程池:

  • Executors.newCachedThreadPool():复用空闲线程
  • Executors.newScheduledThreadPool():支持定时任务

5. ForkJoinTask概述

ForkJoinTask是实现了Future的抽象类,能在少量线程中执行大量任务。其核心特点是:

  • 通过fork()拆分子任务
  • 通过join()合并结果

两个主要实现类:

  • RecursiveTask<V>:返回值的递归任务
  • RecursiveAction:无返回值的递归任务

示例:计算阶乘平方和(如4²+3²+2²+1²=30):

public class FactorialSquareCalculator extends RecursiveTask<Integer> {
 
    private Integer n;

    public FactorialSquareCalculator(Integer n) {
        this.n = n;
    }

    @Override
    protected Integer compute() {
        if (n <= 1) {
            return n;
        }

        FactorialSquareCalculator calculator 
          = new FactorialSquareCalculator(n - 1);

        calculator.fork(); // 异步执行子任务

        return n * n + calculator.join(); // 获取子任务结果
    }
}

使用ForkJoinPool执行:

ForkJoinPool forkJoinPool = new ForkJoinPool();

FactorialSquareCalculator calculator = new FactorialSquareCalculator(10);

forkJoinPool.execute(calculator);

6. 总结

本文全面探讨了Future接口的核心方法及其使用场景,并展示了如何通过线程池实现并行计算。同时简要介绍了ForkJoinTaskfork()join()机制。

相关扩展阅读:

完整示例代码可在GitHub仓库获取。