首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Java并发基础:FutureTask全面解析!

Java并发基础:FutureTask全面解析! - 程序员古德内容概要

FutureTask结合了Future和Runnable接口,它能够异步执行任务,提高程序响应性,可以获取任务执行结果,并且支持任务取消机制,提高了灵活性,同时,它简化了并发编程,使多线程开发更加便捷。

核心概念

FutureTask主要用来解决异步计算的问题,它提供了一种便捷的方式,可以将耗时的计算任务提交给另一个线程去执行,而当前线程可以继续执行其他任务,从而实现并发执行的效果,它实现了Future和Runnable接口,因此它既可以作为Runnable被线程执行,又可以作为Future得到计算结果,当把FutureTask提交给ExecutorService执行后,它会返回一个表示异步计算结果的Future对象,这个Future对象检查计算是否完成,并获取计算的结果。

FutureTask主要用来解决以下问题:

异步执行:当有一个耗时的计算任务,并且不想阻塞当前线程的执行时,可以使用FutureTask将任务提交给另一个线程去执行,这样,当前线程可以继续执行其他任务,提高了整体的执行效率。

获取计算结果:FutureTask提供了检查计算是否完成,并获取计算结果的方法,可以使用Future.get()方法来获取计算结果,如果计算还没有完成,这个方法会阻塞,直到计算完成为止。这使得可以在需要的时候方便地获取异步计算的结果。

异常处理:如果异步计算抛出了异常,那么调用Future.get()方法时会抛出ExecutionException,这样,可以在获取计算结果的同时,方便地处理异步计算中可能出现的异常。

任务取消:通过Future.cancel()方法,可以取消还没有开始的或者正在进行的异步计算,这为提供了更多的灵活性,可以根据需要动态地控制异步计算的执行。

代码案例

import java.util.concurrent.*;

// 定义一个Callable任务,它实现了call()方法,该方法有返回值

class MyCallableTask implements Callable<Integer> {

private int num;

public MyCallableTask(int num) {

this.num = num;

}

@Override

public Integer call() throws Exception {

// 模拟耗时操作,比如计算一个数的平方

int result = num * num;

System.out.println("计算完成: " + result);

return result;

}

}

public class FutureTaskDemo {

public static void main(String[] args) {

// 创建一个ExecutorService,用于执行异步任务

ExecutorService executor = Executors.newSingleThreadExecutor();

// 创建一个Callable任务实例

Callable<Integer> callableTask = new MyCallableTask(5);

// 使用FutureTask包装Callable任务

FutureTask<Integer> futureTask = new FutureTask<>(callableTask);

// 提交FutureTask到ExecutorService中执行,但是这里有个问题:

// executor.submit(futureTask); // 这行代码实际上是错误的,因为futureTask已经被明确指定了任务

// 正确的做法是直接提交Callable任务给ExecutorService,并获得Future引用

Future<Integer> future = executor.submit(callableTask);

// 在这里可以做其他事情,而Callable任务在另一个线程中异步执行

try {

// 获取异步计算的结果,如果计算还没完成,get()方法会阻塞

Integer result = future.get(); // 注意这里使用的是future,而不是futureTask

System.out.println("获取到的结果是: " + result);

} catch (InterruptedException | ExecutionException e) {

// 处理可能的异常

e.printStackTrace();

} finally {

// 关闭ExecutorService

executor.shutdown();

}

}

}

在上述代码中:

MyCallableTask类实现了Callable接口,并覆盖了call()方法,该方法计算一个数的平方并返回结果。

FutureTaskDemo类的main方法中,创建一个ExecutorService实例,用于执行异步任务。

然后,创建一个Callable任务实例,并使用FutureTask将其包装起来,将FutureTask提交到ExecutorService中执行。

最后,通过调用futureTask.get()方法来获取异步计算的结果,如果计算还没完成,get()方法会阻塞,直到结果可用为止。

代码输出如下结果:

计算完成: 25

获取到的结果是: 25

核心API

FutureTask 它实现了 Future 和 Runnable 接口,因此既可以作为异步计算的结果,也可以作为 Runnable 被执行,FutureTask 通常用于那些需要计算结果的异步计算场景,下面是 FutureTask 中一些重要方法的简要说明:

FutureTask(Callable<V> callable) 构造函数

创建一个 FutureTask,该任务将调用给定的 Callable 对象来计算其结果。

void run()

开始执行计算任务,这个方法通常在另一个线程中调用,例如在通过 Thread 类或者 Executor 框架提交任务时。

V get()

获取计算结果,如果计算还没有完成,这个方法会阻塞直到计算完成。

V get(long timeout, TimeUnit unit)

尝试在给定的时间内获取计算结果,如果计算在这段时间内完成,返回结果;否则,抛出 TimeoutException。

boolean isDone()

检查计算是否已经完成,如果完成,返回 true;否则,返回 false。

boolean isCancelled()

检查任务是否被取消,如果任务在开始前被取消,或者在执行过程中被中断并且还没有完成,那么这个方法返回 true。

boolean cancel(boolean mayInterruptIfRunning)

尝试取消任务的执行,如果任务还没有开始,或者如果 mayInterruptIfRunning 为 true 并且任务正在运行,那么这个方法会尝试中断正在执行任务的线程。如果任务被成功取消,返回 true;否则,返回 false。

核心总结

FutureTask允许将耗时的任务放到后台异步执行,不阻塞主线程,提升系统响应性,当任务完成后,可以方便地获取计算结果,无需额外的同步措施,它还提供了取消机制,能够中途终止任务的执行。但是使用FutureTask时需要注意,它的get方法只能调用一次以获取结果,重复调用会抛出异常,如果异步任务中抛出异常,只有在调用get方法时才会抛出ExecutionException,异常处理的时机与位置可能不那么直观。

个人思考

Java并发基础:Phaser全面解析! - 程序员古德

RunnableFuture和Future有什么区别?

在Java中,Future接口和RunnableFuture接口都是与并发编程相关的接口,它们用于处理异步计算的结果,但是他们在使用场景上有这明显的接区别,如下:

Future接口:

Future接口表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。

Future.get()方法用于获取计算的结果。如果计算还没有完成,这个方法会阻塞直到计算完成。

Future.isDone()方法用于检查计算是否已经完成。

Future.cancel()方法用于取消计算,如果计算还没有开始,那么这个计算将不会进行,如果计算已经在进行,那么这个方法可能无法停止计算,具体取决于具体的实现和计算能力。

Future接口不能直接运行,它只是一个表示可能还没有完成的异步计算的结果的占位符,要运行任务并获取Future对象,通常需要将任务提交给ExecutorService。

RunnableFuture接口:

RunnableFuture接口扩展了Runnable和Future接口,这意味着它既可以作为Runnable被线程执行,又可以作为Future来获取执行结果。

RunnableFuture.run()方法是Runnable接口的一部分,用于执行异步计算。

与Future接口一样,RunnableFuture也提供了get(),isDone()和cancel()等方法来检查计算的状态,获取结果或取消计算。

与Future不同的是,RunnableFuture可以被直接执行,这意味着可以创建一个RunnableFuture的实现,将其传递给一个线程来执行,而不必通过ExecutorService。

核心区别

RunnableFuture是Future的一个扩展,它添加了Runnable接口的功能,因此可以被直接执行,这使得RunnableFuture更加灵活,因为它既可以被线程直接执行,又可以作为Future来管理和获取结果。

通常,不会直接使用RunnableFuture,而是使用FutureTask这个RunnableFuture的实现类,FutureTask允许将Callable或Runnable任务封装为一个可以被线程执行的对象,并且可以通过Future接口获取任务的结果。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OARcBulwztHekgLmB4p189PA0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券