我们知道线程池通过execute方法执行提交的Runnable任务,但Runnable只是执行任务,没有返回任何信息。

【线程池原理:线程池原来是个外包公司,打工人我悟了

若是我们想在异步执行完任务后能够拿到结果。怎么处理呢?

我们可以借助Callable来回去返回结果。线程池为我们提供了另外一种方式执行任务,即submit方法

1、为线程池提交任务

  • execute方法执行Runnable任务
  • submit方法执行Runnable或Callable任务,且能获取任务返回结果

2、流程分解

2.1、execute方法执行Runnable任务

execute方法将Runnable任务交付给线程池执行



2.2、submit方法执行Runnable或Callable任务

2.2.1、创建futureTask对象(也是Runnable对象),包含属性Callable和object;将futureTask对象引用传递给外部


public class FutureTask<V> implements RunnableFuture<V> { /**
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
}

FutureTask中的状态属性(state)代表任务执行的进度。当任务执行到最终态时,代表任务执行结束。

后面调用get()方法时,判断到最终态才能获取object的值

2.2.2、若传入Runnable任务,将其转为Callable任务,赋值给Callable;

若传入Callable任务,则直接赋值给Callable

public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
         throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
} /**
* A callable that runs given task and returns given result
  */
static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
final T result;
    RunnableAdapter(Runnable task, T result) {
         this.task = task;
         this.result = result;
     }
    public T call() {
         task.run();
         return result;
     }
 }

2.2.3、futureTask作为Runnable,execute方法执行此任务(见2.1)

2.2.4、当执行futureTask时,会调用其run方法执行Callable任务

Callable任务若正常返回结果则赋值给object;

若执行异常,则将异常捕获并赋值给object

2.2.5、外部根据futureTask对象引用,调用get()方法,获取到futureTask中的object;

区分是返回结果或异常进行处理

public V get() throws InterruptedException, ExecutionException {
      int s = state;
      if (s <= COMPLETING)
          s = awaitDone(false, 0L);
      return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}

通过get()获取结果的过程:

判断任务是否达到最终态。若达到,则根据状态将outcome值区分处理

若未达到

通过LockSupport.parkNanos(futureTask, nanos);挂起当前线程;

futureTask执行到最终态后会执行LockSupport.unpark(thread);重新恢复 因调用get()而挂起的线程

------The End------

如果这个办法对您有用,或者您希望持续关注,也可以扫描下方二维码或者在微信公众号中搜索

线程池系列二:一张动图,彻底懂了execute和submit的更多相关文章

  1. 线程池系列二:ThreadPoolExecutor讲解

    一.简介 1)线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为: ThreadPoolExecutor(int corePoolSize, i ...

  2. 13张动图助你彻底看懂马尔科夫链、PCA和条件概率!

    13张动图助你彻底看懂马尔科夫链.PCA和条件概率! https://mp.weixin.qq.com/s/ll2EX_Vyl6HA4qX07NyJbA [ 导读 ] 马尔科夫链.主成分分析以及条件概 ...

  3. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  4. juc线程池原理(二):ThreadPoolExecutor的成员变量介绍

    概要 线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析ThreadPoolExecutor类,来了解线程池的原理. ThreadPoolExecutor数据结构 Thread ...

  5. Java 线程池(二)

    简介 在上篇 Java 线程池(一) 我们介绍了线程池中一些的重要参数和具体含义,这篇我们看一看在 Java 中是如何去实现线程池的,要想用好线程池,只知其然是远远不够的,我们需要深入实现源码去了解线 ...

  6. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  7. Java之线程池(二)

    关于线程和线程池的学习,我们可以从以下几个方面入手: 第一,什么是线程,线程和进程的区别是什么 第二,线程中的基本概念,线程的生命周期 第三,单线程和多线程 第四,线程池的原理解析 第五,常见的几种线 ...

  8. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  9. java线程池技术(二): 核心ThreadPoolExecutor介绍

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程池技术属于比较"古老"而又比较基础的技术了,本篇博客主要作用是个人技术梳理,没什么新玩意. 一.Java线程池技术的 ...

随机推荐

  1. vim中字符串的替换

    vi/vim 中可以使用 :s 命令来替换字符串 :s/vivian/sky/ 替换当前行第一个 vivian 为 sky :s/vivian/sky/g 替换当前行所有 vivian 为 sky : ...

  2. node实战小例子

    第一章 2020-2-6 留言小本子 思路(由于本章没有数据库,客户提交的数据放在全局变量,接收请求用的是bodyParser, padyParser使用方法 app.use(bodyParser.u ...

  3. 集合遍历数组三种常用方式(Collecton和Map)

    Collection集合遍历数组的三种方式: 迭代器 foreach(增强for循环) JDK1.8之后的新技术Lambda 迭代器: 方法:public Iterator inerator():获取 ...

  4. HDU2063 过山车(二分匹配)

    过山车 HDU - 2063 RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了.可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做part ...

  5. 洛谷P1309——瑞士轮(归并排序)

    https://www.luogu.org/problem/show?pid=1309#sub 题目背景 在双人对决的竞技性比赛,如乒乓球.羽毛球.国际象棋中,最常见的赛制是淘汰赛和循环赛.前者的特点 ...

  6. PHP操作用户提交内容时需要注意的危险函数

    对于我们的程序开发来说,用户的输入是解决安全性问题的第一大入口.为什么这么说呢?不管是SQL注入.XSS还是文件上传漏洞,全部都和用户提交的输入参数有关.今天我们不讲这些问题,我们主要探讨下面对用户的 ...

  7. jmeter之命令行执行jmx脚本

    使用界面执行不稳定,且保存报告非常麻烦 https://www.jb51.net/article/191367.htm 作者:Anthony_tester 来源:CSDN 原文:https://blo ...

  8. Selenium多浏览器并行测试

    如果需要同时在IE.firefox.chrome进行测试,可以使用grid. Selenium Grid是一个智能代理服务器,允许Selenium测试将命令路由到远程Web浏览器实例.其目的是提供一种 ...

  9. 使用Jmeter做接口测试(学生信息的6个接口)

    使用Jmeter做接口测试,案例中涉及到接口有:获取学生信息.登录.添加学生信息.学生金币充值.获取所有学生信息.文件上传. 一.获取学生信息(get请求) 服务器名称或IP:输入被请求服务器的名称或 ...

  10. YbtOJ#532-往事之树【广义SAM,线段树合并】

    正题 题目链接:https://www.ybtoj.com.cn/problem/532 题目大意 给出\(n\)个点的一个\(Trie\)树,定义\(S_x\)表示节点\(x\)代表的字符串 求$$ ...