【异步编程基础】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框架。

相关推荐
Escalating_xu7 小时前
【C 语言】深入理解指针(1·下):指针运算、野指针、assert 与传址实战
java·c语言·开发语言
实心儿儿7 小时前
Qt — Qt 事件
开发语言·qt
周杰偷奶茶7 小时前
【Java】数据类型与变量
java·开发语言
code斗7 小时前
Java数据结构:堆详解
java·开发语言·数据结构
重生之小比特7 小时前
【C++进阶】map和set
java·开发语言·c++
lie..8 小时前
30天从零开始学AI应用开发(Day 12):像产品一样稳:流式输出、报错重试、花钱控制
开发语言·人工智能·php
傻啦嘿哟8 小时前
Python的默认参数把我坑惨了,原来写[]和写None的区别这么大
开发语言·python·机器学习
DeepAgent8 小时前
AI Agent 工程实践(49):一次真实优化——从 Agent v1 到 v2
开发语言·人工智能·agent
知识分享小能手8 小时前
C++ 学习教程,从入门到精通,C++对象和类 — 详细知识点总结(10)
开发语言·c++·学习
郑州光合科技余经理9 小时前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程