【异步编程基础】FutureTask基本原理与异步阻塞问题

文章目录

    • [一、FutureTask 的桥梁作用](#一、FutureTask 的桥梁作用)
    • [二、Future 模式与异步回调](#二、Future 模式与异步回调)
    • [三、 FutureTask获取异步结果的逻辑](#三、 FutureTask获取异步结果的逻辑)
      • [1. 获取异步执行结果的步骤](#1. 获取异步执行结果的步骤)
      • [2. 举例说明](#2. 举例说明)
      • [3. FutureTask的异步阻塞问题](#3. FutureTask的异步阻塞问题)

Runnable 用于定义无返回值的任务,而 Callable 用于定义有返回值的任务。然而,Callable 实例无法直接作为 Thread 线程的 target 目标,因为 Thread 的 target 属性类型为 Runnable,而 Callable 与 Runnable 之间没有任何继承关系。那么,如何将 Callable 与 Thread 结合使用呢?答案是通过 FutureTask。本文将详细介绍 Callable、FutureTask 以及它们在异步编程中的应用。

一、FutureTask 的桥梁作用

FutureTask 是 RunnableFuture 接口的实现类,而 RunnableFuture 继承了 Runnable 和 Future 接口。因此,FutureTask 既可以作为 Thread 的 target,又可以获取异步任务的结果。

如下FutureTask 的核心功能:

java 复制代码
//实现了Future:获取异步执行结果
//实现了Runnable:可以作为`Thread` 线程的目标任务。
//支持取消任务、查询任务是否完成等功能。
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

public class FutureTask<V> implements RunnableFuture<V> {

以下是一个使用 FutureTask 的示例:

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

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

        // 使用 FutureTask 包装 Callable 任务
        FutureTask<String> futureTask = new FutureTask<>(callableTask);

        // 启动一个新线程执行任务
        new Thread(futureTask).start();

        System.out.println("Main thread is doing other work...");

        // 获取任务结果(会阻塞直到任务完成)
        String result = futureTask.get();
        System.out.println("Task result: " + result);
    }
}

Main thread is doing other work...
Task result: Task completed!

二、Future 模式与异步回调

Future 模式是一种异步编程模式,其核心思想是:

  • 异步调用 :任务提交后立即返回一个 Future 对象,表示任务的未来结果。
  • 结果获取 :通过 Future 对象获取任务的执行结果,如果任务未完成,调用线程会阻塞直到任务完成。

Future 接口的核心方法

  • isDone():判断任务是否完成。
  • isCancelled():判断任务是否被取消。
  • cancel(boolean mayInterruptRunning):取消任务的执行。
  • get():获取任务结果(阻塞)。
  • get(long timeout, TimeUnit unit):在指定时间内获取任务结果(超时抛出异常)。

三、 FutureTask获取异步结果的逻辑

1. 获取异步执行结果的步骤

通过FutureTask类和Callable接口的联合使用可以创建能获取异步执行结果的线程。

创建能够返回结果的线程,启动并获取结果。具体的步骤介绍如下:

  1. 创建一个Callable接口的实现类,并实现它的call()方法,编写好异步执行的具体逻辑,并且可以有返回值。
  2. 使用Callable实现类的实例构造一个FutureTask实例。
  3. 使用FutureTask实例作为Thread构造器的target入参,构造新的Thread线程实例。
  4. 调用Thread实例的start()方法启动新线程,启动新线程的run()方法并发执行。
  5. 调用FutureTask对象的get()方法阻塞性地获得并发线程的执行结果。

其内部的执行过程为:启动Thread实例的run()方法并发执行后,会执行FutureTask实例的run()方法,最终会并发执行Callable实现类的call()方法。

2. 举例说明

使用FutureTask实现异步泡茶喝,main线程可以获取烧水线程、清洗线程的执行结果,然后根据结果判断是否具备泡茶条件,如果具备泡茶条件再泡茶。

具体代码见:gitee 并发编程 futureTask demo

几点需要注意:

  1. FutureTask和Callable都是泛型类,泛型参数表示返回结果的类型。所以,在使用时它们两个实例的泛型参数需要保持一致。
  2. 通过FutureTask实例的get方法可以获取线程的执行结果,主线程拿到各个线程的结果,然后判断执行。
  3. 主线程阻塞等待。

3. FutureTask的异步阻塞问题

虽然FutureTask通过 Future 对象管理异步任务的结果,避免阻塞主线程。但通过FutureTask的get()方法获取异步结果时,主线程也会被阻塞。这一点FutureTask和join是一致的,它们都是异步阻塞模式。

异步阻塞的效率往往比较低,被阻塞的主线程不能干任何事情,唯一能干的就是傻傻等待。原生Java API除了阻塞模式的获取结果外,并没有实现非阻塞的异步结果获取方法。

如果需要用到获取的异步结果,得引入一些额外的框架,比如谷歌的Guava框架。

相关推荐
来让爷抱一个15 小时前
Python 异步里混用同步库,事件循环被卡住的全过程
开发语言·python
心祭丫丫17 小时前
【每天一个 Python 小案例17】Python 实现通讯录
开发语言·人工智能·笔记·python·学习
露天赏雪18 小时前
第3篇:Sentinel熔断降级实战:大模型接口抖动时的服务兜底方案
java·开发语言·人工智能·spring boot·spring·servlet·sentinel
yeccheng20 小时前
MySQL 索引超详解:从底层 B+ 树到面试高频考点
java·开发语言·jvm·mysql·面试
联犀20 小时前
纯 Go 的 CAD 开源库 go-cad:直接解析 DWG/DXF,现在也能导出 PDF
开发语言·golang·pdf
zjxtxdy21 小时前
C++面向对象程序开发-类与对象
开发语言·c++
qq_406981031 天前
c++拷贝构造函数
开发语言·c++
爱喝雪碧的可乐1 天前
MATLAB
开发语言·算法·matlab
H.莓飛1 天前
【C++】【STL】vector_OJ题
linux·服务器·开发语言·数据结构·c++·visual studio
j7~1 天前
【C++ 八股文】(篇四)C++11 新特性
开发语言·c++·面试·c++11·八股文