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

文章目录

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

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

一、FutureTask 的桥梁作用

FutureTaskRunnableFuture 接口的实现类,而 RunnableFuture 继承了 RunnableFuture 接口。因此,FutureTask 既可以作为 Threadtarget,又可以获取异步任务的结果。

如下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框架。

相关推荐
喵了meme4 小时前
C语言实战4
c语言·开发语言
码界奇点4 小时前
Python从0到100一站式学习路线图与实战指南
开发语言·python·学习·青少年编程·贴图
9ilk4 小时前
【C++】--- 特殊类设计
开发语言·c++·后端
sali-tec4 小时前
C# 基于halcon的视觉工作流-章68 深度学习-对象检测
开发语言·算法·计算机视觉·重构·c#
生骨大头菜6 小时前
使用python实现相似图片搜索功能,并接入springcloud
开发语言·python·spring cloud·微服务
绝不收费—免费看不了了联系我6 小时前
Fastapi的单进程响应问题 和 解决方法
开发语言·后端·python·fastapi
消失的旧时光-19436 小时前
深入理解 Java 线程池(二):ThreadPoolExecutor 执行流程 + 运行状态 + ctl 原理全解析
java·开发语言
咖啡续命又一天6 小时前
Trae CN IDE 中 Python 开发的具体流程和配置总结
开发语言·ide·python·ai编程
4311媒体网6 小时前
帝国cms调用文章内容 二开基本操作
java·开发语言·php
GSDjisidi7 小时前
东京IT软件会社-(株)GSD|多种技术栈募集,高度人才+20分
开发语言·面试·职场和发展