首页 > 编程语言 >JAVA 线程池之Callable返回结果

JAVA 线程池之Callable返回结果

时间:2023-06-08 18:13:14浏览次数:50  
标签:JAVA get number resultList Callable Future 线程 result

JAVA 线程池之Callable返回结果

原文:https://www.cnblogs.com/hapjin/p/7599189.html

本文介绍如何向线程池提交任务,并获得任务的执行结果。然后模拟 线程池中的线程在执行任务的过程中抛出异常时,该如何处理。

一、执行具体任务的线程类

要想 获得 线程的执行结果,需实现Callable接口。FactorialCalculator 计算 number的阶乘,具体实现如下:

 1 import java.util.concurrent.Callable;
 2 import java.util.concurrent.TimeUnit;
 3 
 4 /**
 5  * Created by Administrator on 2017/9/26.
 6  */
 7 public class FactorialCalculator implements Callable<Integer> {
 8 
 9     private Integer number;
10 
11     public FactorialCalculator(Integer number) {
12         this.number = number;
13     }
14     public Integer call() throws Exception {
15         int result = 1;
16 
17         if (number == 0 || number == 1) {
18             result = 1;
19         }else {
20             for (int i = 2; i < number; i++) {
21                 result *= i;
22                 TimeUnit.MICROSECONDS.sleep(200);
23                 if (i == 5) {
24                     throw new IllegalArgumentException("excepion happend");//计算5以上的阶乘都会抛出异常. 根据需要注释该if语句
25                 }
26             }
27         }
28         System.out.printf("%s: %d\n", Thread.currentThread().getName(), result);
29         return result;
30     }
31 }

上面23行--25行的if语句表明:如果number大于5,那么 if(i==5)成立,会抛出异常。即模拟 执行5 以上的阶乘时,会抛出异常。

二、提交任务的Main类

下面来看看,怎样向线程池提交任务,并获取任务的返回结果。我们一共向线程池中提交了10个任务,因此创建了一个ArrayList保存每个任务的执行结果。

第一行,首先创建一个线程池。第二行,创建List保存10个线程的执行结果 所要存入的地方,每个任务是计算阶乘,因此线程的返回结果是 Integer。而这个结果只要计算出来了,是放在Future里面。

第5-7行,随机生成一个10以内的整数,然后创建一个 FactorialCalculator对象,该对象就是待执行的任务,然后在第8行 通过线程池的submit方法提交。

 1         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
 2         List<Future<Integer>> resultList = new ArrayList<Future<Integer>>();
 3         Random random = new Random();
 4         for (int i = 0; i < 10; i ++) {
 5             int rand = random.nextInt(10);
 6 
 7             FactorialCalculator factorialCalculator = new FactorialCalculator(rand);
 8             Future<Integer> res = executor.submit(factorialCalculator);//异步提交, non blocking.
 9             resultList.add(res);
10         }

需要注意的是:submit方法是个非阻塞方法,参考这篇文章。提交了任务后,由线程池里面的线程负责执行该任务,执行完成后得到的结果最终会保存在 Future里面,正如第8行所示。

As soon as we invoke the submit() method of ExecutorService the Callable are handed over to ExecutorService to execute.
 Here one thing we have to note, the submit() is not blocking. 
So, all of our Callables will be submitted right away to the ExecutorService, and ExecutorService will decide when to execute which callable. 
For each Callable we get a Future object to get the result later.

接下来,我们在do循环中,检查任务的状态---是否执行完成。

 1         do {
 2 //            System.out.printf("number of completed tasks: %d\n", executor.getCompletedTaskCount());
 3             for (int i = 0; i < resultList.size(); i++) {
 4                 Future<Integer> result = resultList.get(i);
 5                 System.out.printf("Task %d : %s \n", i, result.isDone());
 6             }
 7             try {
 8                 TimeUnit.MILLISECONDS.sleep(50);
 9 
10             } catch (InterruptedException e) {
11                 e.printStackTrace();
12             }
13         } while (executor.getCompletedTaskCount() < resultList.size());

第3-6行for循环,从ArrayList中取出 每个 Future,查看它的状态 isDone() ,即:是否执行完毕。

第13行,while结束条件:当所有的线程的执行完毕时,就退出do循环。注意:当线程在执行过程中抛出异常了,也表示线程执行完毕。

获取线程的执行结果

 1         System.out.println("Results as folloers:");
 2         for (int i = 0; i < resultList.size(); i++) {
 3             Future<Integer> result = resultList.get(i);
 4             Integer number = null;
 5 
 6             try {
 7                 number = result.get();// blocking method
 8             } catch (InterruptedException e) {
 9                 e.printStackTrace();
10             } catch (ExecutionException e) {
11                 e.printStackTrace();
12             }
13             System.out.printf("task: %d, result %d:\n", i, number);
14         }

第3行取出每个存储结果的地方:Future,第7行 从Future中获得任务的执行结果。Future.get方法是一个阻塞方法。但前面的do-while循环里面,我们已经检查了任务的执行状态是否完成,因此这里能够很快地取出任务的执行结果。

We are invoking the get() method of Future to get the result. Here we have to remember that, the get() is a blocking method.

任务在执行过程中,若抛出异常,下面语句在获取执行结果时会直接跳到catch(ExecutionException e)中。

number = result.get()

img

但它不影响Main类线程---主线程的执行。

img

整个Main类代码如下:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by Administrator on 2017/9/26.
 */
public class Main {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        List<Future<Integer>> resultList = new ArrayList<Future<Integer>>();
        Random random = new Random();
        for (int i = 0; i < 10; i ++) {
            int rand = random.nextInt(10);

            FactorialCalculator factorialCalculator = new FactorialCalculator(rand);
            Future<Integer> res = executor.submit(factorialCalculator);//异步提交, non blocking.
            resultList.add(res);
        }

        // in loop check out the result is finished
        do {
//            System.out.printf("number of completed tasks: %d\n", executor.getCompletedTaskCount());
            for (int i = 0; i < resultList.size(); i++) {
                Future<Integer> result = resultList.get(i);
                System.out.printf("Task %d : %s \n", i, result.isDone());
            }
            try {
                TimeUnit.MILLISECONDS.sleep(50);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } while (executor.getCompletedTaskCount() < resultList.size());


        System.out.println("Results as folloers:");
        for (int i = 0; i < resultList.size(); i++) {
            Future<Integer> result = resultList.get(i);
            Integer number = null;

            try {
                number = result.get();// blocking method
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.printf("task: %d, result %d:\n", i, number);
        }
        executor.shutdown();
    }
}

在上面 number = result.get(); 语句中,我们 catch了两个异常,一个是InterruptedException,另一个是ExecutionException。因为我们是在main线程内获得任务的执行结果,main线程执行 result.get()会阻塞,如果在阻塞的过程中被(其他线程)中断,则抛出InterruptedException。

若在任务的执行过程中抛出了异常(比如IllegalArgumentException),那么main线程在这里就会catch到ExecutionException。此时,就可以对抛出异常的任务“进行处理”。此外,线程池中执行该任务的线程,并不会因为 该任务在执行过程中抛出了异常而受到影响,该线程 可以继续 接收并运行 下一次提交给它的任务。

图中 task1、task2、task4 返回的结果为空,表明它们抛出了IllegalArgumentException异常。

而要想对异常进行处理,可参考:Java线程池异常处理最佳实践 这篇文章

标签:JAVA,get,number,resultList,Callable,Future,线程,result
From: https://www.cnblogs.com/JaxYoun/p/17467304.html

相关文章

  • Java开发工程师学习日记(十)
    1.谈谈Java线程池使用的优势:(1)Java线程池是一定数量的线程集合,线程的频繁创建与销毁消耗了操作系统与内存的大量资源,使用线程池使得减少了线程创建与销毁的资源浪费。(2)使用线程池可以提高程序的响应速度,通过复用已存在的线程,无需等待新线程的创建便能立即执行。(3)进行线程并发数的......
  • Java语言实现生产者与消费者的消息队列模型(附源码)
    Java构建生产者与消费者之间的生产关系模型,可以理解生产者生产message,存放缓存的容器,同时消费者进行消费需求的消耗,如果做一个合理的比喻的话:生产者消费者问题是线程模型中的经典问题。解决生产者/消费者问题有两种方法:一是采用某种机制保护生产者和消费者之间的同步;二是在生产者和......
  • 一文读懂大厂面试的JAVA基础(集合,面向对象特性,反射,IO,容器)
    整理了操作系统,计算机网络,以及JVM的高频面试题目,对于面试大厂的Android以及后端开发岗位,可以说的是十分必要的部分就是JAVA语言的基础,在整体的内容上我认为有以下的几个部分,我发现任何的学习都是先建立框架体系,再逐个击破,针对Java的基础中包括:(1)Java语言的面向对象的特性(2)Java语言......
  • Java面试题查缺补漏习题,锁的升级,动态代理
    之前我们总结了Java面试题目中的关于计算机网络,操作系统,以及JVM虚拟机,以及Java的相关特性。今天又看了很多面试的视频,对面试的题目进行一下仔细的补充。1.对称加密与非对称加密的区别:非对称加密和对称加密在加密和解密过程、加密解密速度、传输的安全性上都有所不同,具体介绍如下:......
  • Java开发工程师学习日记(九)
    1.TCP与UDP网络传输协议方面:TCP的传输报文形式比UDP的传输形式更加复杂,因此UDP头部只有四个字段,因此传输效率比较,TCP<UDP2.数组或者字符串的null值含义:null表示字段或者变量还没有确定的值,3.IP地址的分类:A类:0.0.0.0~127.255.255.255B类:128.0.0.0~191.255.255.255C类:192.0.0.0~......
  • java 访问ingress https报错javax.net.ssl.SSLHandshakeException: Received fatal al
    一、报错及部署环境Java程序访问测试域名https方法正常,访问生产域名https域名报错,报错如下javax.net.ssl.SSLHandshakeException:Receivedfatalalert:protocol_version测试环境使用KubeSphereingress生产环境使用阿里云ACK服务的ingress配置二、问题原因客户端......
  • Java中枚举类的特殊用法-使用枚举实现单例模式和策略模式
    上面针对枚举类实现单例模式和策略模式有所涉及,下面补充。Java中使用枚举类实现单例模式为什么可以这样做?枚举类不能new,因此保证单例枚举类不能被继承类不加载时,不会实例化使用枚举类创建的单例还有一个好处,就是即使使用反射,也无法打破它的单例性质新建枚举类publicenumSingleEn......
  • Java基础之基础语法与面向对象
    前言小知识Java由Sun公司于1995年推出,2009年Sun公司被Oracle公司收购,取得Java的版权Java之父:JamesGosling(詹姆斯·高斯林) 专业术语JDK:javadevelopmentkit(java开发工具包)JRE:javaruntimeenvironment(java运行环境)JVM:javavirualma......
  • el-api包冲突,java.lang.LinkageError: loader constraints violated when linking ja
    java.lang.LinkageError:loaderconstraintsviolatedwhenlinkingjavax/el/ExpressionFactoryclass严重:Servlet.service()forservletjspthrewexceptionjava.lang.LinkageError:loaderconstraintsviolatedwhenlinkingjavax/el/ExpressionFactoryclassat......
  • java reflection Java 反射,动态绑定
    javareflection,java反射,动态绑定                                       Reflection是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得Java类中......