Bootstrap

19.jdk源码阅读之FutureTask

1. 写在前面

FutureTask 是 Java 并发包中的一个重要类,它实现了 RunnableFuture 接口,结合了 Future 和 Runnable 的特性。FutureTask 通常用于异步任务的执行和结果的获取。
如下几个问题,大家看看有没有思考过,在后面阅读源码的过程中都会解答:

  1. FutureTask 和 Future 有什么区别?
  2. 如何取消一个 FutureTask?
  3. FutureTask 是如何实现线程安全的?
  4. FutureTask 的使用场景有哪些?
  5. FutureTask 和 CompletableFuture 有什么区别?
  6. 如何处理 FutureTask 的异常?
  7. FutureTask 的 run 方法可以多次调用吗?

2. 全局视角

在这里插入图片描述

2.1 Runnable 接口

Runnable 接口是一个函数式接口,定义了一个 run 方法,用于包含需要在新线程中执行的代码。

@FunctionalInterface
public interface Runnable {
    void run();
}

2.2 Future 接口

Future 接口表示一个异步计算的结果,并提供了检查任务状态、获取结果、取消任务等方法。

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

2.3 RunnableFuture 接口

RunnableFuture 接口继承了 Runnable 和 Future 接口,表示一个既可以作为任务执行(Runnable),又可以获取结果(Future)的任务。

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

2.4 FutureTask 的关键方法

FutureTask 实现了 RunnableFuture 接口中的所有方法,并提供了一些额外的方法和机制来管理任务的执行和状态。

2.4.1 构造方法

  • FutureTask(Callable callable):通过 Callable 创建 FutureTask。
  • FutureTask(Runnable runnable, V result):通过 Runnable 创建 FutureTask,并指定任务完成后的结果。

2.4.2 任务执行

void run():执行任务,如果任务已完成或已取消则不执行。

2.4.3 结果获取

  • V get():获取任务的结果,如果任务未完成则阻塞。
  • V get(long timeout, TimeUnit unit):在指定时间内获取任务的结果。

2.4.4. 任务状态

  • boolean isDone():检查任务是否已完成。
  • boolean isCancelled():检查任务是否已取消。
  • boolean cancel(boolean mayInterruptIfRunning):取消任务。

3. 从使用说起

3.1 使用 FutureTask 执行 Callable 任务

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureTaskExample {
    public static void main(String[] args) {
        // 创建一个 Callable 任务
        Callable<String> callableTask = () -> {
            Thread.sleep(1000); // 模拟耗时操作
            return "Task completed";
        };

        // 创建一个 FutureTask
        FutureTask<String> futureTask = new FutureTask<>(callableTask);

        // 提交给 Executor 执行
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(futureTask);

        try {
            // 获取结果(阻塞直到任务完成)
            String result = futureTask.get();
            System.out.println("Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

  1. 创建一个 Callable 任务,模拟一个耗时操作。
  2. 使用 Callable 创建一个 FutureTask 实例。
  3. 将 FutureTask 提交给 ExecutorService 执行。
  4. 通过 FutureTask 的 get 方法获取任务的结果,该方法会阻塞直到任务完成。
  5. 最后关闭 ExecutorService。

3.2 使用 FutureTask 执行 Runnable 任务并获取结果

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureTaskRunnableExample {
    public static void main(String[] args) {
        // 创建一个 Runnable 任务
        Runnable runnableTask = () -> {
            try {
                Thread.sleep(1000); // 模拟耗时操作
                System.out.println("Runnable task completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        // 使用 Runnable 和结果值创建一个 FutureTask
        FutureTask<String> futureTask = new FutureTask<>(runnableTask, "Task finished");

        // 提交给 Executor 执行
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(futureTask);

        try {
            // 获取结果(阻塞直到任务完成)
            String result = futureTask.get();
            System.out.println("Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}
  1. 创建一个 Runnable 任务,模拟一个耗时操作。
  2. 使用 Runnable 和任务完成后的结果值创建一个 FutureTask 实例。
  3. 将 FutureTask 提交给 ExecutorService 执行。
  4. 通过 FutureTask 的 get 方法获取任务的结果,该方法会阻塞直到任务完成。
  5. 最后关闭 ExecutorService。

3.3 取消 FutureTask

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureTaskCancelExample {
    public static void main(String[] args) {
        // 创建一个 Callable 任务
        Callable<String> callableTask = () -> {
            Thread.sleep(3000); // 模拟耗时操作
            return "Task completed";
        };

        // 创建一个 FutureTask
        FutureTask<String> futureTask = new FutureTask<>(callableTask);

        // 提交给 Executor 执行
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(futureTask);

        try {
            // 等待一段时间后取消任务
            Thread.sleep(1000);
            boolean cancelled = futureTask.cancel(true);
            System.out.println("Task cancelled: " + cancelled);

            // 获取任务状态
            if (!futureTask.isCancelled()) {
                String result = futureTask.get();
                System.out.println("Result: " + result);
            } else {
                System.out.println("Task was cancelled.");
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}

  1. 创建一个 Callable 任务,模拟一个耗时操作。
  2. 使用 Callable 创建一个 FutureTask 实例。
  3. 将 FutureTask 提交给 ExecutorService 执行。
  4. 等待一段时间后,通过 FutureTask 的 cancel 方法取消任务。
  5. 检查任务是否被取消,如果未取消则获取任务结果,否则输出任务已取消的信息。
  6. 最后关闭 ExecutorService。

4. FutureTask 和 Future 有什么区别?

  • Future 是一个接口,表示一个异步计算的结果。
  • FutureTask 是 Future 的一个具体实现,结合了 Future 和 Runnable 的特性,可以被提交给 Executor 执行,也可以直接在线程中执行。

5. 如何取消一个 FutureTask?

可以调用 FutureTask 的 cancel 方法来取消任务:

futureTask.cancel(true);  // 取消任务,true 表示如果任务正在运行,可以中断它

6. FutureTask 是如何实现线程安全的?

FutureTask 使用了内部状态和同步机制来确保线程安全。例如,它使用 CAS 操作和 volatile 变量来管理任务的状态(如未开始、正在运行、已完成、已取消等)。

7. FutureTask 的使用场景有哪些?

  • 异步计算:可以在后台执行耗时任务,并在需要时获取结果。
  • 并行处理:可以将多个任务并行执行,并在所有任务完成后处理结果。
  • 延迟计算:可以延迟执行任务,直到需要结果时再执行。

8. FutureTask 和 CompletableFuture 有什么区别?

  • FutureTask 是 Java 5 引入的,用于表示一个可取消的异步任务。
  • CompletableFuture 是 Java 8 引入的,提供了更丰富的功能,如链式调用、组合多个异步任务、处理异步任务的结果等。

9. 如何处理 FutureTask 的异常?

可以在获取结果时处理异常:

try {
    String result = futureTask.get();
} catch (InterruptedException e) {
    // 处理线程中断异常
} catch (ExecutionException e) {
    // 处理任务执行异常
    Throwable cause = e.getCause();  // 获取实际异常原因
}

10. FutureTask 的 run 方法可以多次调用吗?

FutureTask 的 run 方法只能被调用一次。多次调用 run 方法不会重新执行任务,只会返回相同的结果或状态。

系列文章

1.JDK源码阅读之环境搭建

2.JDK源码阅读之目录介绍

3.jdk源码阅读之ArrayList(上)

4.jdk源码阅读之ArrayList(下)

5.jdk源码阅读之HashMap

6.jdk源码阅读之HashMap(下)

7.jdk源码阅读之ConcurrentHashMap(上)

8.jdk源码阅读之ConcurrentHashMap(下)

9.jdk源码阅读之ThreadLocal

10.jdk源码阅读之ReentrantLock

11.jdk源码阅读之CountDownLatch

12.jdk源码阅读之CyclicBarrier

13.jdk源码阅读之Semaphore

14.jdk源码阅读之线程池(上)

15.jdk源码阅读之线程池(下)

16.jdk源码阅读之ArrayBlockingQueue

17.jdk源码阅读之LinkedBlockingQueue

18.jdk源码阅读之CopyOnWriteArrayList

;