资讯专栏INFORMATION COLUMN

(十八)java多线程之Callable Future

stormgens / 2942人阅读

摘要:本人邮箱欢迎转载转载请注明网址代码已经全部托管有需要的同学自行下载引言前面我们讲了那么多有关线程的知识不知道读者有没有想过这么一个问题如果有这么一个比较耗时的任务必须使用线程来执行但是在这个任务执行完之后我需要得到这个线程的返回值以目前我们

本人邮箱:
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载

引言

前面我们讲了那么多有关线程的知识.不知道读者有没有想过这么一个问题,如果有这么一个比较耗时的任务,必须使用线程来执行,但是在这个任务执行完之后,我需要得到这个线程的返回值.以目前我们学到的知识,具体实现,我这里不说,大家自行发挥.除此之外,如果线程发生了非运行时异常,我们在主线程就会收到一堆错误信息.还有我们也无法判断任务是否执行完成,有些人会说用Thread1.isAlive()就可以判断任务是否执行完成.这是错的,因为isAlive只是判断线程是否存活,而无法判断任务是否完成,两者是不一样的概念.如果有没有不明白的,请考虑使用线程池ThreadPoolExecutor的情况.
以上说了那么多,其实就是为了引入今天要讲的Callable,Future,FutureTask.ok,让我们看一下这些类是干什么的.

理论

*Callable 可以说是 Runnable的升级版本,既有抛出错误也有返回类型.
*Future 是执行异步任务后的返回值,这个接口包括一下几个方法

* `cancel(boolean mayInterruptIfRunning)` 取消任务,如果`mayInterruptIfRunning`为`true`,即使该任务在运行也可以被中断.否则在运行中的任务不能被取消.
* `isCancelled`判断任务是否被取消
* `isDone` 判断任务是否完成
* `get` 获取任务的返回值,如果任务有异常,则在调用这个方法的时候被抛出
* `get(long timeout, TimeUnit unit)` 在指定时间内等待获取任务的返回值,如果任务有异常,则在调用这个方法的时候被抛出

FutureTaskRunnableFuture的子类

例子1 获取异步任务的返回值 Runnable 版本
public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main start");
        FutureTask task = new FutureTask(() -> System.out.println("执行子任务"), "hello");
        new Thread(task).start();
        while (task.isDone());
        System.out.println("任务返回结果:" + task.get());
        System.out.println("main end");
    }
}

运行结果

main start
执行子任务
任务返回结果:hello
main end

public FutureTask(Runnable runnable, V result) 这个比较适合固定任务返回固定值的情况,如果返回的值需要进过计算,所以有多个情况,则不适合使用这个构造.

例子1 获取异步任务的返回值 Callable 版本
public class Demo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main start");
        FutureTask task = new FutureTask(() -> {
            int sum = 0;
            for (int i = 1; i <= 100; i++){
                sum += i;
            }
            return sum;
        });
        new Thread(task).start();
        while (task.isDone());
        System.out.println("任务返回结果:" + task.get());
        System.out.println("main end");
    }
}

返回结果

main start
任务返回结果:5050
main end

例子3 测试运行时异常情况
public class Demo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main start");
        FutureTask task = new FutureTask(() -> {
            System.out.println("正在执行子任务");
            int i = 1 / 0;
            return 0;
        });
        new Thread(task).start();
        while (task.isDone());
        System.out.println("任务返回结果:" + task.get());
        System.out.println("main end");
    }
}

运行结果:

main start
正在执行子任务
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.kco.test18.demo.Demo3.main(Demo3.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ArithmeticException: / by zero
at com.kco.test18.demo.Demo3.lambda$main$0(Demo3.java:14)
at com.kco.test18.demo.Demo3$$Lambda$1/27095111.call(Unknown Source)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:745)

需要注意的是,这是抛出的异常是在调用task.get()才抛出的,如果把task.get()注释掉,是不会抛出异常的.所以我们就可以对异常做一下自定义处理.比如写到日志中.

例子4 取消正在执行的任务
public class Demo4 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main start");
        FutureTask task = new FutureTask(() -> {
            System.out.println("start 正在执行子任务");
            Thread.sleep(500);
            System.out.println("end 执行子任务");
            return 0;
        });
        new Thread(task).start();
        Thread.sleep(250);
        task.cancel(true);
        System.out.println("main end");
    }
}

运行结果

main start
start 正在执行子任务
main end

发现子任务确实运行运行了一般,然后被取消了.

补充

上一篇讲到了ThreadPoolExecutor,我们在执行任务的时候只使用了execute,这个是没有返回值的.而且如果任务抛出异常,则会直接在主线程打印出错误堆栈的.其实ThreadPoolExecutor还有另外一个提交任务的方法,就是submit(Runnable task, T result)submit(Callable task),使用的就是我们这章节所讲的内容.大家可以自行写例子测试这两个方法.

打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69971.html

相关文章

  • Java线程进阶(四二)—— J.U.Cexecutors框架:Future模式

    摘要:本文首发于一世流云的专栏一模式简介模式是多线程设计模式中的一种常见模式,它的主要作用就是异步地执行任务,并在需要的时候获取结果。二中的模式在多线程基础之模式中,我们曾经给出过模式的通用类关系图。 showImg(https://segmentfault.com/img/bVbiwcx?w=1000&h=667); 本文首发于一世流云的专栏:https://segmentfault.co...

    marek 评论0 收藏0
  • Java线程线程安全与异步执行

    摘要:同步包装器任何集合类使用同步包装器都会变成线程安全的,会将集合的方法使用锁加以保护,保证线程的安全访问。线程池中的线程执行完毕并不会马上死亡,而是在池中准备为下一个请求提供服务。 多线程并发修改一个数据结构,很容易破坏这个数据结构,如散列表。锁能够保护共享数据结构,但选择线程安全的实现更好更容易,如阻塞队列就是线程安全的集合。 线程安全的集合 Vector和HashTable类提供了线...

    taoszu 评论0 收藏0
  • 初读《Java并发编程的艺术》-第十章:Executor框架 -10.1 Executor框架简介

    摘要:线程的启动与销毁都与本地线程同步。操作系统会调度所有线程并将它们分配给可用的。框架的成员主要成员线程池接口接口接口以及工具类。创建单个线程的接口与其实现类用于表示异步计算的结果。参考书籍并发编程的艺术方腾飞魏鹏程晓明著 在java中,直接使用线程来异步的执行任务,线程的每次创建与销毁需要一定的计算机资源开销。每个任务创建一个线程的话,当任务数量多的时候,则对应的创建销毁开销会消耗大量...

    aisuhua 评论0 收藏0
  • Java 8 并发教程:线程和执行器

    摘要:在这个示例中我们使用了一个单线程线程池的。在延迟消逝后,任务将会并发执行。这是并发系列教程的第一部分。第一部分线程和执行器第二部分同步和锁第三部分原子操作和 Java 8 并发教程:线程和执行器 原文:Java 8 Concurrency Tutorial: Threads and Executors 译者:BlankKelly 来源:Java8并发教程:Threads和Execut...

    jsdt 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<