【小家Java】Future与FutureTask的区别与联系

简介: 【小家Java】Future与FutureTask的区别与联系

Future模式简述


     传统单线程环境下,调用函数是同步的,必须等待程序返回结果后,才可进行其他处理。 Futrue模式下,调用方式改为异步。


    Futrue模式的核心在于:充分利用主函数中的等待时间,利用等待时间处理其他任务,充分利用计算机资源。


所谓异步调用其实就是实现一个可无需等待被调用函数的返回值而让操作继续运行的方法。在 Java 语言中,简单的讲就是另启一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结果。但调用者仍需要取线程的计算结果。


JDK5新增了Future接口,用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果。


Future


Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果等操作。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。


Future类位于java.util.concurrent包下,它是一个接口:


public interface Future<V> {
    /**
     * 方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
     *
     * @param mayInterruptIfRunning 表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。
     * @return 如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;
     * 如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;
     * 如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
     */
    boolean cancel(boolean mayInterruptIfRunning);
    /**
     * 方法表示任务是否被取消成功
     * @return 如果在任务正常完成前被取消成功,则返回 true
     */
    boolean isCancelled();
    /**
     * 方法表示任务是否已经完成
     * @return 若任务完成,则返回true
     */
    boolean isDone();
    /**
     * 方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
     * @return 任务执行的结果值
     * @throws InterruptedException
     * @throws ExecutionException
     */
    V get() throws InterruptedException, ExecutionException;
    /**
     * 用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null(并不是抛出异常,需要注意)。
     * @param timeout 超时时间
     * @param unit 超时单位
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     * @throws TimeoutException
     */
    V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

从上面方法的注释可以看出,Futrue提供了三种功能:

1)判断任务是否完成;

2)能够中断任务;

3)能够获取任务执行结果。(最为常用的)


因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask(JDK8以前唯一实现类)。


FutureTask


public class FutureTask<V> implements RunnableFuture<V>
  public interface RunnableFuture<V> extends Runnable, Future<V>


由此看出FutureTask它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

FutureTask提供了2个构造器:


  // 包装callable
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
  //把Runable转换成callable来处理(结果尽然让传进来,所以这个方法没啥用)
  public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }


FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。


FutureTask实现了Futrue可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。

举个栗子


直接使用Futrue来接收返回值:(结合callable)


    public static void main(String args[]) {
        Instant start = Instant.now();
        ExecutorService executor = Executors.newCachedThreadPool();
        //执行callable任务  拿到但绘制result
        Future<Integer> result = executor.submit(() -> {
            System.out.println("子线程在进行计算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        });
        Instant mid = Instant.now();
        System.out.println("Mid拿到Futrue结果对象result:" + Duration.between(start, mid).toMillis());
        try {
            System.out.println("task运行结果计算的总和为:" + result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        executor.shutdown();
        Instant end = Instant.now();
        System.out.println("End拿到结果值:" + Duration.between(start, end).toMillis());
    }
输出:
子线程在进行计算
Mid拿到Futrue结果对象result:98
task运行结果计算的总和为:4950
End拿到结果值:3099


我们很显然可以发现,mid的时间很短,主线程马上向下执行了,但是end的时间就比较长了,因此get()拿结果属于阻塞方法。


FutureTask获取结果:(结合Callbale使用)
    public static void main(String args[]) {
        Instant start = Instant.now();
        ExecutorService executor = Executors.newCachedThreadPool();
        //使用FutureTask包装callbale任务,再交给线程池执行
        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
            System.out.println("子线程在进行计算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        });
        //线程池执行任务 这个返回值不需要了,直接就在futureTask对象里面了
        executor.submit(futureTask);
        Instant mid = Instant.now();
        System.out.println("Mid拿到futureTask结果对象result:" + Duration.between(start, mid).toMillis());
        try {
            System.out.println("task运行结果计算的总和为:" + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        executor.shutdown();
        Instant end = Instant.now();
        System.out.println("End拿到结果值:" + Duration.between(start, end).toMillis());
    }
输出:
子线程在进行计算
Mid拿到futureTask结果对象result:86
task运行结果计算的总和为:4950
End拿到结果值:3088


第二种方式(只是进行了简单包装而已):


//第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
            System.out.println("子线程在进行计算");
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        });
        Thread thread = new Thread(futureTask);
        thread.start();


如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。


Callable和Futrue的区别:Callable用于产生结果,Future用于获取结果


总结


Futrue的使用和FutrueTask的使用,没有本质的区别。所以…


相关文章
|
4月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
587 1
|
5月前
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
2265 102
|
6月前
|
存储 缓存 人工智能
Java int和Integer的区别
本文介绍了Java中int与Integer的区别及==与equals的比较机制。Integer是int的包装类,支持null值。使用==比较时,int直接比较数值,而Integer比较对象地址;在-128至127范围内的Integer值可缓存,超出该范围或使用new创建时则返回不同对象。equals方法则始终比较实际数值。
222 0
|
6月前
|
安全 算法 Java
Java 中 synchronized 与 AtomicInteger 的区别
在Java多线程编程中,`synchronized`和`AtomicInteger`均用于实现线程安全,但原理与适用场景不同。`synchronized`是基于对象锁的同步机制,适用于复杂逻辑和多变量同步,如银行转账;而`AtomicInteger`采用CAS算法,适合单一变量的原子操作,例如计数器更新。二者各有优劣,应根据具体需求选择使用。
202 0
|
7月前
|
算法 Java 数据库连接
Java 与 C++ 区别深入剖析及应用实例详解
本文深入剖析了Java和C++两种编程语言的区别,从编译与执行机制、面向对象特性、数据类型与变量、内存管理、异常处理等方面进行对比,并结合游戏开发、企业级应用开发、操作系统与嵌入式开发等实际场景分析其特点。Java以跨平台性强、自动内存管理著称,适合企业级应用;C++则因高性能和对硬件的直接访问能力,在游戏引擎和嵌入式系统中占据优势。开发者可根据项目需求选择合适语言,提升开发效率与软件质量。附面试资料链接:[点此获取](https://pan.quark.cn/s/4459235fee85)。
707 0
|
7月前
|
存储 Java C语言
Java List 复制:浅拷贝与深拷贝方法及区别
我是小假 期待与你的下一次相遇 ~
755 1
|
7月前
|
人工智能 Java API
Java并发编程之Future与FutureTask
本文深入解析了Future接口及其实现类FutureTask的原理与使用。Future接口定义了获取任务结果、取消任务及查询任务状态的规范,而FutureTask作为其核心实现类,结合了Runnable与Future的功能。文章通过分析FutureTask的成员变量、状态流转、关键方法(如run、set、get、cancel等)的源码,展示了异步任务的执行与结果处理机制。最后,通过示例代码演示了FutureTask的简单用法,帮助读者更直观地理解其工作原理。适合希望深入了解Java异步编程机制的开发者阅读。
146 0
|
8月前
|
Java
Java 中 Exception 和 Error 的区别
在 Java 中,`Exception` 和 `Error` 都是 `Throwable` 的子类,用于表示程序运行时的异常情况。`Exception` 表示可被捕获和处理的异常,分为受检异常(Checked)和非受检异常(Unchecked),通常用于程序级别的错误处理。而 `Error` 表示严重的系统级问题,如内存不足或 JVM 错误,一般不建议捕获和处理。编写程序时应重点关注 `Exception` 的处理,确保程序稳定性。
274 0
|
9月前
|
Java 编译器 程序员
java中重载和多态的区别
本文详细解析了面向对象编程中多态与重载的概念及其关系。多态是OOP的核心,分为编译时多态(静态多态)和运行时多态(动态多态)。编译时多态主要通过方法重载和运算符重载实现,如Java中的同名方法因参数不同而区分;运行时多态则依赖继承和方法重写,通过父类引用调用子类方法实现。重载是多态的一种形式,专注于方法签名的多样性,提升代码可读性。两者结合增强了程序灵活性与扩展性,帮助开发者更好地实现代码复用。
395 0