Java 并发执行框架的优势之一是支持运行并发任务,并在任务处理后返回单个结果。Java 并发 API 通过 CallableFuture 两个接口实现了这一功能。

1. Java Callable 和 Future 接口

1.1 Callable 接口

Callable 接口定义了 call() 方法,我们需要在该方法中实现具体的任务逻辑。Callable 是一个泛型接口,这意味着我们必须指定 call() 方法将返回的数据类型。

1.2 Future 接口

Future 接口提供了获取 Callable 任务生成结果以及管理任务状态的方法。

2. Java Callable 与 Future 示例

在本示例中,我们创建了一个实现 Callable 接口的 FactorialCalculator 类。这意味着我们将重写其 call() 方法,并在计算完成后从该方法返回结果。随后,主程序可以通过保存的 Future 引用检索此结果。

// FactorialCalculator.java
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class FactorialCalculator implements Callable<Integer> {

    private Integer number;

    public FactorialCalculator(Integer number) {
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
        int result = 1;
        if ((number == 0) || (number == 1)) {
            result = 1;
        } else {
            for (int i = 2; i <= number; i++) {
                result *= i;
                TimeUnit.MILLISECONDS.sleep(20);
            }
        }
        System.out.println("Result for number - " + number + " -> " + result);
        return result;
    }
}

现在,让我们使用两个线程和 4 个数字来测试上面的阶乘计算器。

// CallableExample.java
package com.howtodoinjava.demo.multithreading;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CallableExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);

        List<Future<Integer>> resultList = new ArrayList<>();

        Random random = new Random();

        for (int i = 0; i < 4; i++) {
            Integer number = random.nextInt(10);
            FactorialCalculator calculator = new FactorialCalculator(number);
            Future<Integer> result = executor.submit(calculator);
            resultList.add(result);
        }

        for (Future<Integer> future : resultList) {
            try {
                System.out.println("Future result is - " + " - " + future.get() + "; And Task done is " + future.isDone());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        // shut down the executor service now
        executor.shutdown();
    }
}

程序输出示例:

Result for number - 4 -> 24
Result for number - 6 -> 720
Future result is -  - 720; And Task done is true
Future result is -  - 24; And Task done is true
Result for number - 2 -> 2
Result for number - 6 -> 720
Future result is -  - 720; And Task done is true
Future result is -  - 2; And Task done is true

在此示例中,我们使用 submit() 方法将 Callable 对象提交给执行器(Executor)执行。该方法接收一个 Callable 对象作为参数,并返回一个 Future 对象。Future 对象主要用于以下两个目的:

  • 控制任务状态:我们可以取消任务或检查任务是否完成。为此,我们使用 isDone() 方法来检查任务状态。
  • 获取返回结果:我们可以通过 call() 方法获得返回的结果。为此,我们使用 get() 方法。该方法会阻塞直到 Callable 对象完成 call() 方法的执行并返回结果。

如果线程在 get() 方法等待结果时被中断,它将抛出 InterruptedException 异常。如果 call() 方法执行过程中抛出异常,则 get() 方法会抛出 ExecutionException 异常。

Future 接口还提供了另一个 get() 方法的重载版本:get(long timeout, TimeUnit unit)。如果在指定时间内结果仍不可用,该方法将抛出 TimeoutException 异常,而不是无限期等待。

参考:http://images.jsdiff.com/archives/15507075

说明:本文示例基于 Java 5 及以上版本,CallableFuture 核心接口行为在后续版本中保持稳定。