首页 > 编程语言 >深入理解 Callable 和 Future:异步编程的强大工具

深入理解 Callable 和 Future:异步编程的强大工具

时间:2024-11-21 18:16:17浏览次数:3  
标签:异步 Runnable Callable 任务 Future 线程 执行

在多线程编程中,CallableFuture 提供了一种强大的方式来处理异步任务,它们解决了 Runnable 无法返回结果以及无法处理异常的问题。通过 CallableFuture,你可以实现更加高效和灵活的线程管理。本篇博客将详细探讨 CallableRunnable 的区别,Future 的作用以及如何利用这些工具提高程序的性能。

1. CallableRunnable 的不同

首先,让我们了解 RunnableCallable 两者的区别:

Runnable 的定义:

public interface Runnable {
    public abstract void run();
}

Callable 的定义:

public interface Callable<V> {
    V call() throws Exception;
}

主要区别:

  1. 返回值

    • Runnablerun() 方法没有返回值,适用于不需要返回结果的任务。
    • Callablecall() 方法有返回值,适用于需要返回结果的任务。
  2. 异常处理

    • Runnablerun() 方法不能抛出受检查异常(checked exceptions)。
    • Callablecall() 方法可以抛出受检查异常(checked exceptions),这使得它更适合复杂的任务。
  3. 应用场景

    • 如果你的任务不需要返回值或抛出异常,Runnable 是足够的。
    • 如果你需要任务的结果或任务执行过程中可能会抛出异常,Callable 是必选的。

2. 为什么需要 Callable

Runnable 在设计时并没有考虑到需要返回结果或处理异常的情况。我们可以使用一些其他的间接方法(如写入共享资源或日志文件)来获取 Runnable 执行的结果,但这种方式非常笨拙且低效。

Callable 设计的目标是解决以下问题:

  • 任务返回值Callable 能直接返回任务执行的结果,不需要额外的步骤。
  • 异常处理Callable 能够抛出异常,使得异常管理更加方便和规范。

3. CallableFuture 的关系

Future 是一个接口,它表示异步计算的结果。通过 Future,你可以获取 Callable 任务的执行结果,判断任务是否完成,甚至取消任务。Callable 任务的结果就是通过 Future 获取的。

Future 的作用

Future 主要用于处理长时间运行的任务。我们可以将任务交给后台线程执行,而主线程继续处理其他任务,待后台任务完成后通过 Future 获取结果。

CallableFuture 的关系

  • Callable 的任务通过 Future 进行管理。
  • Future 提供了方法来获取任务结果(get())、判断任务是否完成(isDone())、取消任务(cancel())等功能。

4. Future 的方法和用法

Future 接口定义了五个方法,帮助管理异步任务的执行:

1. get() 获取结果

get() 方法用于获取任务的执行结果。它会阻塞当前线程,直到任务完成并返回结果。若任务执行过程中抛出异常,get() 会抛出 ExecutionException

V get() throws InterruptedException, ExecutionException;
  • 阻塞:如果任务尚未完成,get() 会阻塞当前线程,直到任务完成。
  • 异常处理:任务执行过程中抛出的异常,会被包装成 ExecutionException 抛出。

2. isDone() 判断任务是否执行完毕

该方法用于判断任务是否已完成,不会阻塞线程。

boolean isDone();
  • 如果任务已完成(无论成功、失败还是取消),返回 true,否则返回 false

3. cancel() 取消任务

cancel() 方法用于取消任务的执行。

boolean cancel(boolean mayInterruptIfRunning);
  • 如果任务尚未开始执行,则取消任务并返回 true
  • 如果任务正在执行,可以选择是否中断执行,mayInterruptIfRunning 参数决定是否中断正在执行的任务。

4. isCancelled() 判断任务是否被取消

该方法用于判断任务是否被取消。

boolean isCancelled();
  • 如果任务已被取消,返回 true,否则返回 false

5. 使用 FutureTask 来创建 Future

FutureTaskRunnableFuture 接口的实现,它既实现了 Runnable 接口(可以交给线程池执行),也实现了 Future 接口(可以获取任务的执行结果)。FutureTask 既能作为任务执行,也能作为 Future 获取任务结果。

FutureTask 示例:

public class FutureTaskDemo {
    public static void main(String[] args) {
        Task task = new Task();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
        new Thread(integerFutureTask).start();
        try {
            System.out.println("Task result: " + integerFutureTask.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}

在上面的示例中,FutureTaskCallable 任务包装为一个 Runnable 对象,可以通过 FutureTask 获取任务的结果。

6. Future 使用注意事项

  • 批量获取结果时容易阻塞:当使用 Future 批量获取任务的结果时,可能会造成线程阻塞。建议为 get() 方法设置 timeout 限制。
  • 生命周期不能后退:一旦任务完成,Future 的状态无法重置。任务完成后,Future 不能重新启动。
  • 并不会自动创建线程CallableFuture 本身并不创建线程,它们依赖于 Thread 或线程池来执行任务。

7. 实际应用:旅游平台的异步处理

假设你在开发一个旅游平台,需要从多个供应商那里获取不同的价格数据。使用 Future 结合 Callable 可以帮助你异步执行多个任务,并在所有任务完成后统一获取结果。

CountDownLatch 示例:

public class CountDownLatchDemo {
    ExecutorService threadPool = Executors.newFixedThreadPool(3);

    public static void main(String[] args) throws InterruptedException {
        CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo();
        System.out.println(countDownLatchDemo.getPrices());
    }

    private Set<Integer> getPrices() throws InterruptedException {
        Set<Integer> prices = Collections.synchronizedSet(new HashSet<Integer>());
        CountDownLatch countDownLatch = new CountDownLatch(3);
        threadPool.submit(new Task(123, prices, countDownLatch));
        threadPool.submit(new Task(456, prices, countDownLatch));
        threadPool.submit(new Task(789, prices, countDownLatch));
        countDownLatch.await(3, TimeUnit.SECONDS);
        return prices;
    }

    private class Task implements Runnable {
        Integer productId;
        Set<Integer> prices;
        CountDownLatch countDownLatch;

        public Task(Integer productId, Set<Integer> prices, CountDownLatch countDownLatch) {
            this.productId = productId;
            this.prices = prices;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            int price = 0;
            try {
                Thread.sleep((long) (Math.random() * 4000));
                price = (int) (Math.random() * 4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            prices.add(price);
            countDownLatch.countDown();
        }
    }
}

在这个例子中,CountDownLatch 被用来等待所有的任务完成后再返回结果。

总结

CallableFuture 提供了强大的功能来执行异步任务和管理任务的执行结果。相比于 RunnableCallable 允许任务有返回值,并且可以处理异常。而 Future 则为任务提供了取消、查询执行状态等管理功能。通过合理使用 CallableFuture,你可以更高效地处理并发任务,提高程序的运行效率和稳定性。

希望本篇文章能帮助你深入理解 CallableFuture,并在项目中灵活运用!

标签:异步,Runnable,Callable,任务,Future,线程,执行
From: https://blog.csdn.net/fulai00/article/details/143858754

相关文章

  • 异步编程在ArkTS中具体怎么实现?
    大家好,我是V哥,很好奇,在ArkTS中实现异步编程是怎样的,今天的内容来聊聊这个问题,总结了一些学习笔记,分享给大家,在ArkTS中实现异步编程主要可以通过以下几种方式:1.使用async和await关键字async函数是一种特殊的函数,它能以同步代码的方式编写异步代码。在async函数内部,可以使用aw......
  • 模拟线程池与异步方法调用查询接口优化
    问题:批量查询如何优化?entity实体类packagecom.itheima.alipay.prop;importlombok.Data;@DatapublicclassUserInfo{privateLonguserId;privateStringusername;privateintage;publicUserInfo(LonguserId,Stringusername,intage){......
  • Abp.VNext-异步执行帮助类AsyncHelper
    作用以同步的方式运行异步方法。代码实现//无返回值的异步方法publicasyncTaskGetDataNoResult(){awaitTask.CompletedTask;}//有返回值的异步方法publicasyncTask<bool>GetDataWithResult(){returnawaitTask.FromResult(true);}[Htt......
  • Abp.VNext-异步执行器AsyncExecuter
    作用方便在应用服务层对IQueryable执行异步操作。代码实现varqueryable=await_ordedrRepository.WithDetailAsync(x=>x.OrderItems);queryable=queryable.WhereIf(inputDto.Guids.Any(),x=>inputDto.GuidIds.Contains(x.Id));varpageQueryable=queryable.OrderBy(......
  • .net 非阻塞的异步编程 及 线程调度过程
    本文主要分为三个部分:1、语法格式2、线程调度情况3、编程注意事项*阅读提示:鼠标悬停在章节标题上可见文章目录  异步编程(TaskAsynchronousProgramming,TAP),一种编程模式(Task-basedAsynchronousPattern)。TAP是.NET中推荐的异步编程模式,基于 Task 和 Task<TR......
  • 数据通信的基础概念,串行、并行、半双工、全双工、同步异步的区分与定义
    在通信领域中,串行、并行、半双工、全双工、同步和异步是描述数据传输方式和特性的重要概念。以下是对这些通信方式的区分与定义(本文结合原子教程以及自己的一些学习笔记综合而成):按照数据通信方式可以分为串行、并行通信:串行通信:数据逐位按照顺序依次传输并行通信:数据各位通......
  • java 创建线程的三种方法(Thread,Runnable,Callable)ExecutorService
    1.继承Thread类2.实现Runnable接口3.实现Callable接口4.线程池1.继承Thread类packagecom.chen;//创建线程的方式:继承Thread,重写run(),调用start()开启线程//注意,线程开启不一定立即执行,由cpu调度执行publicclassTestThread2extendsThread{@Overridepublicvoid......
  • unity3d————场景异步加载
    总结本文介绍了Unity中场景切换的两种方法:同步切换和异步切换。同步切换在切换场景时会删除当前场景的所有对象并加载下一个场景的信息,可能导致卡顿。因此,异步切换被引入来解决这个问题。异步切换有两种实现方式:通过事件回调函数和通过协程。通过事件回调函数实现异步加载代......
  • 同步和异步
    一、javascript中为什么需要引入异步?  1.我们知道js是单线程语言,只能同时做一件事。但在遇到需要等待(网络请求,定时任务)不能卡住,所以引入了异步  2.异步就是为了解决单线程需要等待的问题(如:网络请求,定时任务) 3.异步是基于回调callback函数形式 二、同步和异步1.异步......
  • Python并发编程入门:使用concurrent.futures与asyncio
    Python并发编程入门:使用concurrent.futures与asyncio在现代应用中,并发编程已成为一种提升性能和效率的重要手段。Python提供了多种实现并发的方式,尤其是concurrent.futures和asyncio,分别适用于不同的并发场景。本文将带你深入了解这两种并发编程方式,帮助你轻松上手并......