`
zy116494718
  • 浏览: 476854 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Future详解

    博客分类:
  • Java
 
阅读更多

一般使用线程池执行任务都是调用的execute方法,这个方法定义在Executor接口中:

public interface Executor {
    void execute(Runnable command);
}

这个方法是没有返回值的,而且只接受Runnable。

那么像得到线程的返回值怎嘛办呢?

在ExecutorService接口中能找到这个方法:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

这个方法接收两种参数,Callable和Runnable。返回值是Future。

下面具体看一下这些是什么东西。

Callable和Runnable

先看一下两个接口的定义:

  • Callable

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

    interface Runnable {
        public abstract void run();
    }
    

和明显能看到区别:

  1. Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值
  2. Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常。

Future

返回值Future也是一个接口,通过他可以获得任务执行的返回值。

定义如下:

public interface Future<V> {
    boolean cancel(boolean var1);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long var1, TimeUnit var3) throws InterruptedException, ExecutionException, TimeoutException;
}

其中的get方法获取的就是返回值。

来个例子

submit(Callable<T> task)

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        //创建一个Callable,3秒后返回String类型
        Callable myCallable = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                System.out.println("calld方法执行了");
                return "call方法返回值";
            }
        };
        System.out.println("提交任务之前 "+getStringDate());
        Future future = executor.submit(myCallable);
        System.out.println("提交任务之后,获取结果之前 "+getStringDate());
        System.out.println("获取返回值: "+future.get());
        System.out.println("获取到结果之后 "+getStringDate());
    }
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        String dateString = formatter.format(currentTime);
        return dateString;
    }
}

通过executor.submit提交一个Callable,返回一个Future,然后通过这个Future的get方法取得返回值。

看一下输出:

提交任务之前 12:13:01
提交任务之后,获取结果之前 12:13:01
calld方法执行了
获取返回值: call方法返回值
获取到结果之后 12:13:04

get()方法的阻塞性

通过上面的输出可以看到,在调用submit提交任务之后,主线程本来是继续运行了。但是运行到future.get()的时候就阻塞住了,一直等到任务执行完毕,拿到了返回的返回值,主线程才会继续运行。

这里注意一下,他的阻塞性是因为调用get()方法时,任务还没有执行完,所以会一直等到任务完成,形成了阻塞。

任务是在调用submit方法时就开始执行了,如果在调用get()方法时,任务已经执行完毕,那么就不会造成阻塞。

下面在调用方法前先睡4秒,这时就能马上得到返回值。

System.out.println("提交任务之前 "+getStringDate());
Future future = executor.submit(myCallable);
System.out.println("提交任务之后 "+getStringDate());
Thread.sleep(4000);
System.out.println("已经睡了4秒,开始获取结果 "+getStringDate());
System.out.println("获取返回值: "+future.get());
System.out.println("获取到结果之后 "+getStringDate());
提交任务之前 12:36:04
提交任务之后 12:36:04
calld方法执行了
已经睡了4秒,开始获取结果 12:36:08
获取返回值: call方法返回值
获取到结果之后 12:36:08

可以看到吗,因为睡了4秒,任务已经执行完毕,所以get方法立马就得到了结果。

同样的原因,submit两个任务时,总阻塞时间是最长的那个。

例如,有两个任务,一个3秒,一个5秒。

Callable myCallable = new Callable() {
    @Override
    public String call() throws Exception {
        Thread.sleep(5000);
        System.out.println("calld方法执行了");
        return "call方法返回值";
    }
};
Callable myCallable2 = new Callable() {
    @Override
    public String call() throws Exception {
        Thread.sleep(3000);
        System.out.println("calld2方法执行了");
        return "call2方法返回值";
    }
};
System.out.println("提交任务之前 "+getStringDate());
        Future future = executor.submit(myCallable);
        Future future2 = executor.submit(myCallable2);
        System.out.println("提交任务之后 "+getStringDate());
        System.out.println("开始获取第一个返回值 "+getStringDate());
        System.out.println("获取返回值: "+future.get());
        System.out.println("获取第一个返回值结束,开始获取第二个返回值 "+getStringDate());
        System.out.println("获取返回值2: "+future2.get());
        System.out.println("获取第二个返回值结束 "+getStringDate());

输出

提交任务之前 14:14:47
提交任务之后 14:14:48
开始获取第一个返回值 14:14:48
calld2方法执行了
calld方法执行了
获取返回值: call方法返回值
获取第一个返回值结束,开始获取第二个返回值 14:14:53
获取返回值2: call2方法返回值
获取第二个返回值结束 14:14:53

获取第一个结果阻塞了5秒,所以获取第二个结果立马就得到了。

submit(Runnable task)

因为Runnable是没有返回值的,所以如果submit一个Runnable的话,get得到的为null:

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " run time: " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

Future future = executor.submit(myRunnable);
        System.out.println("获取的返回值: "+future.get());

输出为:

pool-1-thread-1 run time: 1493966762524
获取的返回值: null

submit(Runnable task, T result)

虽然submit传入Runnable不能直接返回内容,但是可以通过submit(Runnable task, T result)传入一个载体,通过这个载体获取返回值。这个其实不能算返回值了,是交给线程处理一下。

先新建一个载体类Data:

public static class Data {
    String name;
    String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

然后在Runnable的构造方法中传入:

static class MyThread implements Runnable {
    Data data;

    public MyThread(Data name) {
        this.data = name;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println("线程  执行:");
            data.setName("新名字");
            data.setSex("新性别");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后调用:

Data data = new Data();
Future<Data> future = executor.submit(new MyThread(data), data);
System.out.println("返回的结果  name: " + future.get().getName()+", sex: "+future.get().getSex());
System.out.println("原来的Data  name: " + data.getName()+", sex: "+data.getSex());

输出:

线程  执行:
返回的结果  name: 新名字, sex: 新性别
原来的Data  name: 新名字, sex: 新性别

发现原来的data也变了。

get(long var1, TimeUnit var3)

前面都是用的get()方法获取返回值,那么因为这个方法是阻塞的,有时需要等很久。所以有时候需要设置超时时间。

get(long var1, TimeUnit var3)这个方法就是设置等待时间的。

如下面的任务需要5秒才能返回结果:

Callable myCallable = new Callable() {
    @Override
    public String call() throws Exception {
        Thread.sleep(5000);
        return "我是结果";
    }
};

使用get:

Future future1 = executor.submit(myCallable);
System.out.println("开始拿结果 "+getStringDate());
System.out.println("返回的结果是: "+future1.get()+ " "+getStringDate());
System.out.println("结束拿结果 "+getStringDate());

输出是:

开始拿结果 16:00:43
返回的结果是: 我是结果 16:00:48
结束拿结果 16:00:48

现在要求最多等3秒,拿不到返回值就不要了,所以用get(long var1, TimeUnit var3)这个方法

方法的第一个参数是长整形数字,第二个参数是单位,跟线程池ThreadPoolExecutor的构造方法里一样的。

Future future1 = executor.submit(myCallable);
System.out.println("开始拿结果 "+getStringDate());
try {
    System.out.println("返回的结果是: "+future1.get(3, TimeUnit.SECONDS)+ " "+getStringDate());
} catch (TimeoutException e) {
    e.printStackTrace();
    System.out.println("超时了 "+getStringDate());
}
System.out.println("结束拿结果 "+getStringDate());

然后输出是

过了三秒就抛出超时异常了,主线程继续运行,不会再继续阻塞。

异常

使用submit方法还有一个特点就是,他的异常可以在主线程中catch到。

而使用execute方法执行任务是捕捉不到异常的。

用下面这个Runnable来说,这个 里面一定会抛出一个异常

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        executor.execute(null);
    }
};

使用execute

这里如果捕捉到异常,只打印一行异常信息。

try {
            executor.execute(myRunnable);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("抓到异常 "+e.getMessage());
        }

输出

并没有出现抓到异常哪行日志。而且这个异常输出是在线程pool-1-thread-1中,并不是在主线程中。说明主线程的catch不能捕捉到这个异常。

使用submit

try {
           Future future1= executor.submit(myCallable);
            future1.get();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("抓到异常 "+e.getMessage());
        }

输出

 

这个就能抓到异常了。

分享到:
评论

相关推荐

    Python库 | im_future-0.1.5-py2.py3-none-any.whl

    《Python库im_future详解》 在Python的生态系统中,丰富的第三方库是其强大的驱动力之一。今天我们将聚焦于一个名为`im_future`的库,它在版本0.1.5中提供,支持Python 2和Python 3环境,具体表现为`im_future-...

    C++11中std::future的具体使用方法

    在C++11标准库中,`std::future`是一个重要的工具,用于处理异步编程。它提供了一种方式来获取在另一个线程或者异步操作中计算的结果,确保了线程安全的数据访问。`std::future`的核心特性是它引用的共享状态是唯一...

    31 凭票取餐—Future模式详解.pdf

    【31 凭票取餐—Future模式详解】 在Java并发编程中,Future模式是一种常见的设计模式,它允许主线程在不阻塞的情况下启动一个异步任务,然后在需要时获取任务的结果。Future模式的灵感来源于现实生活中的场景,如...

    future-0.17.1.tar.gz

    《Python未来库(Future)详解及安装指南》 在Python编程中,`future`库是一个不可或缺的工具,它为Python 2和Python 3之间的互操作性提供了强大的支持。`future-0.17.1.tar.gz`是该库的一个版本压缩包,包含了在...

    future-0.18.2-py3-none-any.whl 官网太难下载了

    《Python的Future模块详解及其0.18.2版本特性》 在Python编程中,`future`库是一个不可或缺的工具,它为Python 2和Python 3之间的代码兼容提供了便利。`future-0.18.2-py3-none-any.whl`是一个针对Python 3的whl...

    java Future 接口使用方法详解

    Java中的`Future`接口是Java并发编程的重要组成部分,它提供了对异步计算结果的访问和控制。`Future`接口在`java.util.concurrent`包中,主要用于处理由`ExecutorService`执行的任务。`Future`模式允许我们提交一个...

    java Callable与Future的详解及实例

    Callable 和 Future 是 Java 多线程编程中的两个关键接口,它们在 Java 5 及以上版本引入,以增强并发处理的能力。Callable 接口类似 Runnable,但提供了更丰富的功能,而 Future 对象则用于管理和控制异步计算的...

    Python模块future用法原理详解

    这篇文章主要介绍了Python模块future用法原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 计算机的知识太多了,很多东西就是一个使用过程中详细积累的...

    future-0.18.2-py3-none-any.zip

    《Python的Future模块详解——基于future-0.18.2-py3-none-any.whl的探讨》 在Python编程中,"future"模块扮演着一个至关重要的角色,尤其是在处理Python 2到Python 3的过渡阶段。"future-0.18.2-py3-none-any.whl...

    图解CompletableFuture不创建线程的使用场景方法详解.docx

    面分析了 Future,通过它我们可以获取线程的执行结果,它虽然解决了 Runnable 的 “三无” 短板,但是它自身还是有短板: 不能手动完成计算 假设你使用 Future 运行子线程调用远程 API 来获取某款产品的最新价格,...

    Spring Boot对Future模式的支持详解

    Spring Boot对Future模式的支持使得开发者可以更方便地在应用程序中实现异步处理,从而提高系统的并发性能和响应速度。在实际开发中,当遇到需要执行耗时操作或复杂计算时,我们可以利用多线程来提升效率,而Future...

    Python库 | future_fstrings-0.4.1.tar.gz

    《Python库future_fstrings-0.4.1详解》 在Python编程中,字符串处理是一项基本且频繁的任务,尤其在动态生成代码或者日志记录时。`future_fstrings`是Python社区开发的一个库,旨在为Python 2和Python 3提供一致的...

    Spring Boot利用@Async异步调用:使用Future及定义超时详解

    本文将详细介绍如何使用`@Async`结合`Future`来处理异步任务,并探讨如何定义超时。 `@Async`是Spring提供的一个注解,用于标记一个方法为异步执行。当一个带有`@Async`的方法被调用时,Spring会创建一个新的线程来...

    python中利用await关键字如何等待Future对象完成详解

    ### Python中利用`await`关键字等待`Future`对象完成详解 #### 前言 随着异步编程在现代软件开发中的重要性日益增加,Python语言也在不断地改进其处理并发任务的能力。从Python 3.5版本开始,Python引入了新的语法...

    Java多线程详解

    以上是对"Java多线程详解"主题的详细阐述,涵盖了Java多线程的基本概念、实现方式、线程控制、线程池、并发集合、线程间通信以及并发编程中常见的问题和解决方案。学习和熟练掌握这些内容对于开发高效的多线程Java...

    Guava-并行编程Futures详解.pdf

    《Guava中的并行编程与Futures详解》 在Java并行编程中,Google的Guava库提供了一套强大的工具,极大地丰富了Future接口的功能,尤其是ListenableFuture接口及其相关的扩展。这些工具使得异步计算的管理和处理变得...

    Android应用开发详解pdf和源代码

    《Android应用开发详解》这本书是Android开发者们的重要参考资料,它深入浅出地讲解了Android应用开发的各个环节。书中不仅涵盖了Android开发的基础知识,还包含了大量实践案例,旨在帮助初学者快速上手,同时也能...

    详解JDK中ExecutorService与Callable和Future对线程的支持

    Java并发编程中的ExecutorService、Callable和Future Java并发编程中,ExecutorService、Callable和Future是三大核心组件,它们之间紧密相连,共同实现了高效、安全的并发编程。下面我们将详细介绍这些组件的作用和...

    Java多线程详解(超详细)_狂神说笔记完整版_项目代码_适合小白随课程学习

    Java多线程详解 在Java编程中,多线程是一种重要的技术,它使得程序能够同时执行多个任务,提高系统的效率和响应性。本教程将详细讲解Java中的多线程概念,包括线程的创建、状态、同步以及高级主题,旨在帮助初学者...

Global site tag (gtag.js) - Google Analytics