Executor框架(七)Future 接口、FutureTask类

Future接口介绍

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

??Future 一般由 ExecutorService 的submit()、invokeAll()方法返回的,用于跟踪、获取任务在线程池中的运行情况、等待运算结果,还可以取消任务。(还有其子接口 ScheduleFuture 则由 ScheduleExecutorService 的schedule()等方法返回);

方法描述

boolean cancel(boolean mayInterruptIfRunning):

试图取消对此任务的执行。分成以下三种情况:

  • 如果任务尚未启动,则此任务将永不运行。
  • 如果任务已经启动,则 mayInterruptIfRunning 参数确定是否 中断这个任务来尝试停止任务。同时,任务也应该要对中断敏感。
  • 任务已完成、或已取消,或者由于某些其他原因而无法取消,返回false。

注意: 此方法返回后,对 isDone() 的后续调用将始终返回 true。但如果此方法返回 true,则对 isCancelled() 的后续调用才将始终返回

boolean isCancelled(): 如果在任务正常完成前将其取消,则返回 true。

boolean isDone(): 如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。

获取计算结果

??获取计算结果的方法,JDK提供了两个方法:阻塞获取 与 超时等待获取。这两个方法会抛出 CancellationException(任务被取消时)、InterruptedException

V get(): 等待计算完成,然后获取其结果。

V get(long timeout,TimeUnit unit): 最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。

@ Example 示例

??下面的例子,是在单线程的线程池中提交两个任务(任务A、任务B)。

public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
    //单线程的线程池
    ExecutorService executor = Executors.newSingleThreadExecutor();
    //提交两个任务
    Future futureA = executor.submit(new MyCallable("futureA"));
    Future futureB = executor.submit(new MyCallable("futureB"));

    Thread.sleep(1000);
    //在运行一秒后,判断任务A是否完成
    if(futureA.isDone()){
        //如果完成,则直接获取结果
        double result  = (double) futureA.get();
        System.out.println("运算结果是:"+result);
    }else{
        //如果没有完成,则取消任务A
        boolean b = futureA.cancel(false);
        System.out.println("futureA 执行了cancel方法,返回的值是:"+b);
    }
    //取消任务B
    futureB.cancel(false);
}
}

class MyCallable implements Callable{

    String taskName;

    public MyCallable(String taskName){
        this.taskName = taskName;
    }

    @Override
    public Object  call() {
        try {
            //模拟任务的执行时间为 2s
            for(int i=0;i<5;i++){
                Thread.sleep(400);
                System.out.println(taskName+"任务正在运行中.....");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        double d = Math.random() * 10;
        return d;
    }
}

运行结果:

futureA任务正在运行中.....

futureA任务正在运行中.....

futureA 执行了cancel方法,返回的值是:true

futureA任务正在运行中.....

futureA任务正在运行中.....

futureA任务正在运行中.....

??任务A是在执行时被取消的,调用的cancel(false) 方法返回的结果为true,但是任务并没有真的停止执行。任务B则是在还没被执行时取消的,所以任务B在后续的时间内,没有执行。

??可以得出结论,cancel( false)方法是取消尚未被执行的任务、周期任务,而不是停止正在执行的任务。当然,如果想要停止正在执行的任务,任务里面必须是中断敏感,然后 cancel(true),参数为true,即在cancel的同时,也发出中断信号。

//简单的中断处理,发现中断退出
public void run(){
    while(!Thread.interrupted()){
        //.....
    }
}

FutureTask 介绍

??FutureTask 是一个可取消的异步计算任务,是一个独立的类,实现了 Future、Runnable接口。FutureTask 的出现是为了弥补 Thread 的不足而设计的,可以让程序员跟踪、获取任务的执行情况、计算结果

??因为 FutureTask 实现了 Runnable,所以 FutureTaskk 可以作为参数来创建一个新的线程来执行,也可以提交给 Executor 执行。FutureTask 一旦计算完成,就不能再重新开始或取消计算。

构造方法

FutureTask(Callable

创建一个 FutureTask,一旦运行就执行给定的 Callable。

FutureTask(Runnable runnable, V result)

创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。

应用场景

FutureTask 可用于异步获取执行结果或可以取消执行任务的场景;

@ Example 简单例子

??下面的例子中,因为计算数据的时间比较长,所以main线程就额外起一个异步线程来计算数据,从而使得计算数据的同时,main线程可以做其他工作,直到需要用到计算结果时,才去获取计算结果。

??需要注意的是,线程 thread2 并没有执行 FutureTask,因为 FutureTask 已经在线程 thread 中完成了。一旦 FutureTask 计算完成,就不能再重新开始或取消计算。

public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
    FutureTask<Double> task = new FutureTask(new MyCallable());
    //创建一个线程,异步计算结果
    Thread thread = new Thread(task);
    thread.start();
    //主线程继续工作
    Thread.sleep(1000);
    System.out.println("主线程等待计算结果...");
    //当需要用到异步计算的结果时,阻塞获取这个结果
    Double d = task.get();
    System.out.println("计算结果是:"+d);

    //用同一个 FutureTask 再起一个线程
    Thread thread2 = new Thread(task);
    thread2.start();
}
}

class MyCallable implements Callable<Double>{

    @Override
    public Double call() {
         double d = 0;
         try {
             System.out.println("异步计算开始.......");
              d = Math.random()*10;
             d += 1000;
            Thread.sleep(2000);
             System.out.println("异步计算结束.......");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return d;
    }
}

运行结果:

异步计算开始.......

主线程等待计算结果...

异步计算结束.......

计算结果是:1002.7806590582911

除了实现Future、Runnable外,此类还提供了几个protected方法,用于扩展此类

protected void done()

当此任务转换到状态 isDone(不管是正常地还是通过取消)时,调用受保护的方法。默认实现不执行任何操作。

protected void set(V v)

除非已经设置了此 Future 或已将其取消,否则将其结果设置为给定的值。在计算成功完成时通过 run 方法内部调用此方法。

protected void setException(Throwable t)

除非已经设置了此 Future 或已将其取消,否则它将报告一个 ExecutionException,并将给定的 throwable 作为其原因。在计算失败时通过 run 方法内部调用此方法。

protected boolean runAndReset()

执行计算而不设置其结果,然后将此 Future 重置为初始状态,如果计算遇到异常或已取消,则该操作失败。本操作被设计用于那些本质上要执行多次的任务。

原文地址:https://www.cnblogs.com/jinggod/p/8490458.html

时间: 2024-08-29 20:44:35

Executor框架(七)Future 接口、FutureTask类的相关文章

Java学习笔记33(集合框架七:Collections工具类)

数组有工具类,方面操作数组 集合也有工具类:Collections 常用方法示例: package demo; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class CollectionDemo { public static void main(String[] args) { function1(); function2(); function3(); } p

Java并发编程系列之十五:Executor框架

Java使用线程完成异步任务是很普遍的事,而线程的创建与销毁需要一定的开销,如果每个任务都需要创建一个线程将会消耗大量的计算资源,JDK 5之后把工作单元和执行机制区分开了,工作单元包括Runnable和Callable,而执行机制则由Executor框架提供.Executor框架为线程的启动.执行和关闭提供了便利,底层使用线程池实现.使用Executor框架管理线程的好处在于简化管理.提高效率,还能避免this逃逸问题--是指不完整的对象被线程调用. Executor框架使用了两级调度模型进行

Java线程池Executor框架详解

Java的线程既是工作单元,也是执行机制.从JDK 5开始,把工作单元与执行机制分离开来.工作单元包括Runnable和Callable,而执行机制由Executor框架提供. Executor框架简介在HotSpot VM的线程模型中,Java线程(java.lang.Thread)被一对一映射为本地操作系统线程.Java线程启动时会创建一个本地操作系统线程:当该Java线程终止时,这个操作系统线程也会被回收.操作系统会调度所有线程并将它们分配给可用的CPU.在上层,Java多线程程序通常把应

java多线程之Executor框架线程池详细介绍与ThreadPoolExecutor

Executor框架简介 Executor框架的结构 Executor框架主要由3大部分组成: 任务: 包括被执行的任务需要实现的接口:Runable 接口.Callable接口: 任务的执行: 包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口.Executor框架有两个关键类实现了ExecutorService接口:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor.ForkJoinPool

Java并发编程-Executor框架(转)

本文转自http://blog.csdn.net/chenchaofuck1/article/details/51606224 感谢作者 我们在传统多线程编程创建线程时,常常是创建一些Runnable对象,然后创建对应的Thread对象执行它们,但是如果程序需要并发执行大量的任务时,需要为每个任务都创建一个Thread,进行管理,这将会影响程序的执行效率,并且创建线程过多将会使系统负载过重. 在JDK 1.5之后通过了一套Executor框架能够解决这些问题,能够分解任务的创建和执行过程.该框架

concurrent包分析之Executor框架

文章目录 线程生命周期的开销:线程比较少的情况使用new Thread(task)无多大影响,但是如果涉及到线程比较多的情况,应用的性能就会受到影响,如果jdbc创建连接一样,new Thead创建线程也会耗资源.耗时间的. 资源的消耗量:活动线程会消耗系统性能,如果运行的线程数量多余可用的处理器数,那么就会有大量空闲的线程占用内存,会给垃圾收集器带来压力,如果有cpu资源竞争,还会有其他性能开销. 限定创建线程的数目:如果不设定创建线程的数量,一个任务一个线程无限创建线程,高负载情况下就有可能

Java并发编程:Future接口、FutureTask类

在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callable.Future和FutureTask三个类的使用方法.以下是本文的目录

Java并发编程-Executor框架之Callable和Future接口

在上一篇文章中我们已经了解了Executor框架进行线程管理,这篇文章将学习Executor框架的另一个特性,我们知道执行Runnable任务是没有返回值得,但Executor可以运行并发任务并获得返回值,Concurrent包提供下面两个接口实现这个功能: Callable接口:这个接口声明call(),类似于Runnable的run(),可以在这个方法里实现任务的具体逻辑操作.Callable是一个泛型接口,必须声明call()的返回类型. Future接口:这个接口声明了一下方法来获取Ca

Java线程与并发编程实践----并发工具类与Executor框架

java5之前,我们使用诸如synchronized,wait(),notify()方法对线程的操作属于对 底层线程的操作,这样会出现很多的问题: 低级的并发原语,比如synchronized,wait(),notify()经常难以正确使用.误用会导致 竞态条件,线程饿死,死锁等风险. 泰国依赖synchronized会影响程序性能以及程序的可扩展性 开发者经常需要高级线程结构,如线程池,信号量.java对底层线程的操作不包含这些结. 为解决这些问题,java5引入并发工具类,该工具类主要有下面