JAVA线程池submit详解 ,execute和submit提交任务的区别

news2024/11/8 18:01:36

文章目录

  • 前言
  • submit方法定义
  • Future是什么
  • execute、submit方法区别是什么
  • submit主干流程逻辑
  • newTaskFor做了什么
    • FutureTask
    • newTaskFor(Runnable runnable, T value)的实现
    • FutureTask(Callable<V> callable)的实现
    • execute(ftask)
  • FutureTask是如何实现线程池执行可获取返回值的
    • 那FutureTask的run方法做了什么事情呢?
      • run方法
      • setException(Throwable t)
      • set(V v)
    • 将结果(或异常)赋值到outcome 后,如何获取?

前言

在上一篇文章中,已介绍了线程池ThreadPoolExecutor的概念,运行流程,注意事项以及实战,以及详细拆解分析了线程池任务提交方法execute()。

ThreadPoolExecutor类本身是没有submit方法的,但其继承了AbstractExecutorService这个线程池抽象类,这个抽象类呢又实现了ExecutorService接口,submit方法,正是ExecutorService接口中的抽象类,这一点,在上篇博文类图分析中也有展示

image-20230621111003968

具体可查看博文:万字详解-JAVA线程池分析实战与利弊详解

从类图得知,submit()一系列方法,属于线程池次顶层接口ExecutorService 的抽象方法,且该接口与execute()方法不同的是其不仅可以接收RunnableCallable 甚至还可以有返回值。

通读了上一篇文章后,我们应该清楚,线程池execute方法提交的任务执行后是没有返回值的,那么这个submit居然可以获取返回值,是如何实现的呢?我们带着疑问,走进源码。

submit方法定义

submit是接口ExecutorService中的几个重载抽象方法

参数为callable的方法,返回一个Future

<T> Future<T> submit(Callable<T> task);

参数为Runnable 和预设结果 的方法,返回一个Future(runnable执行结束后,返回设置的result)

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

参数为Runnable的方法,返回一个Future

Future<?> submit(Runnable task);

image-20230621113447468

image-20230621113804812

callable和runable一样,都是一个函数式接口(都可以使用Lambda表达式来实现方法)且都是实现线程的方式,区别就是callable可以获取返回值以及抛出异常

那我们如何界定我们调用的submit(Callable task) 方法 还是 submit(Runnable task)方法呢?

很简单,就看我们的lambda代码段逻辑中是否含有返回值就行了

ex:

() -> System.out.println(111) ,这段lambda代码块没有返回值,故此调用的是submit(Runnable task)

Future<?> submit = threadPool.submit(() -> System.out.println(111));

() -> 111 ,这段lambda代码块有返回值,故此调用的是submit(Callable task)

Future<Integer> submit = threadPool.submit(() -> 111);

Future是什么

image-20230621115201499

点击源码查看,Future也是一个接口,其有五个抽象方法,分别是取消任务 、判断是否已取消任务、判断任务是否完成、获取任务结果(一直阻塞等待直至获取到结果)、指定时间内获取任务结果

image-20230621115305933

其有这169+具体实现类,后边我们根据线程池submit的使用,再仔细剖析其中的个别具体实现


execute、submit方法区别是什么

二者的应用场景不同

execute

侧重于异步任务执行,且与调用线程无联动性,只需要提交了后让线程池慢慢消费就好了,任务的结果与调用者线程无关联和依赖。

流程示例Ex:

调用者线程 > 提交线程池执行异步任务(不关心异步任务是否有返回值以及返回值结果)

场景:

调用者线程进行逻辑校验,校验通过后,提交线程池进行短信发送

submit

既侧重与异步执行,又侧重于获取执行后的结果做逻辑处理 或者多个异步任务之间有着关联性

流程示例Ex:

调用者线程 > 提交线程池执行异步任务(关心异步执行结果)可能提交多个异步任务 > 调用者线程拿到多个异步任务结果后再进行逻辑处理

场景:

查询一个复杂的业务数据(这里假设是驾驶员)

调用者线程 判断是否有这个驾驶员 > 提交线程池执行异步任务(任务1 查询从业证 耗时300ms,任务2 查询车辆信息耗时 300ms,任务3查询驾驶证信息 耗时400ms)>等待所有异步任务执行完毕,调用者线程判断任务结果拼接数据返回前端

submit主干流程逻辑

ExecutorService接口的实现类有如下

image-20230621140312069

AbstractExecutorServiceThreadPoolExecutor的父类,且ThreadPoolExecutor没有再次覆写submit相关方法,因此我们用 ThreadPoolExecutor中submit系列方法,实际就是使用的父类 AbstractExecutorService 中的方法,所以我们故此我们从这里入手

下方是submit的3个实现

image-20230621141119269

其主干流程很简洁明了

1 根据 Runnable 或 Callable构建RunnableFuture

2 execute执行逻辑

3 返回 RunnableFuture

newTaskFor做了什么

newTaskFor 是将我们的Runnable 或者 Callable 转换为Future或Future的实现,在AbstractExecutorService 中是转为了FutureTask

image-20230621142702872

FutureTask

public class FutureTask<V> implements RunnableFuture<V> {}

FutureTask一种可取消的异步计算类。这个类提供了Future的基本实现,其中包含启动和取消计算、查询计算是否完成以及检索计算结果的方法

下方是FutureTask 这个类的方法展示以及树结构图,我们先简单过一下类中的方法与类结构图,后边会继续详解

image-20230621143239269

image-20230621143949052

上边有一些关键字段信息

public class FutureTask<V> implements RunnableFuture<V> {
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
	// 要执行的任务,执行完后会清理
    private Callable<V> callable;
    // 任务执行的结果 (也可能是异常)
    private Object outcome; 
  
    private volatile Thread runner;
    private volatile WaitNode waiters;
}

newTaskFor(Runnable runnable, T value)的实现

image-20230621142818760

做的事情主要是有两个

1、将runnable转换为callable

调用了Executors.callable方法,将 runnable转换为callable

image-20230621144205499

其实际就是继续将Runnable 使用 RunnableAdapter包裹了一下

自定义了一个RunnableAdapter类去实现 Callable,内部依然存的是runnable,和参数传入的预设结果

image-20230621144415548

2、设置FutureTask的状态

设置FutureTask状态为新建

FutureTask(Callable callable)的实现

image-20230621144745103

做的事情也是两个

1、将我们的callable设置到FutureTask的字段

2、设置FutureTask状态为新建

execute(ftask)

execute方法,调用的是顶层Executor类中execute (Runnable command)方法

image-20230621150229421

那如果使用ThreadPoolExecutor submit的话则实际就是使用 ThreadPoolExecutor类中 execute方法

image-20230621150336524

那么execute执行的就是 execute(futureTask)

ThreadPoolExecutor类中 execute方法执行逻辑 可以参考我的上篇博文:万字详解-JAVA线程池分析实战与利弊详解

问题就来了,ThreadPoolExecutor执行execute是没有返回值的,我们使用FutureTask包了一下Runnable或者Callable就可以拿到返回值呢?

谜底就在谜面上,FutureTask内部逻辑为我们做了处理

FutureTask是如何实现线程池执行可获取返回值的

还记得上方的FutureTask类结构图吗? FutureTask就是一个Runnable的子类

而我们线程池执行execute会先创建Worker,然后执行Worker

Worker是啥?Worker不也是Runnable的一个包装么,最终都会执行run()方法,我们的FutureTask也一样,在线程池执行的时候,会调用run()方法

那FutureTask的run方法做了什么事情呢?

image-20230621152523438

run方法

从主干流程来说比较简单 判断状态 > 执行callable > 捕获了异常,将结果设置到outcome >清理工作

public void run() {
    // 判断线程状态
    if (state != NEW ||
        !RUNNER.compareAndSet(this, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            // 结果
            V result;
            // 是否成功执行
            boolean ran;
            try {
                // 执行call (执行我们的业务逻辑)
                result = c.call();
                // 设置成功执行标签为true
                ran = true;
            } catch (Throwable ex) 
                //特别注意的是,这里捕获了异常
                // 设置
                result = null;
                // 设置成功执行标签为false
                ran = false;
                // 设置异常信息
                setException(ex);
            }
            if (ran)
                // 成功执行了任务,设置结果
                set(result);
        }
    } finally {
        // 清理工作,清理线程和状态
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

setException(Throwable t)

这个方法是在FutureTask执行任务时,但出现了异常被捕获进入

把Throwable 赋值到了outcome (将异常设置到了结果字段中)

image-20230621154204932

set(V v)

这个方法是在FutureTask 成功执行后进入,用来为结果字段outcome 赋值

image-20230621154558826

将结果(或异常)赋值到outcome 后,如何获取?

获取结果,简单来说就是获取outcome字段的值

无超时时间,阻塞当前调用者线程,直至获取到结果

image-20230621155435146

有超时时间,阻塞当前调用者线程一定时间,时间到了未获取到则为null

image-20230621155510016

image-20230621155149920

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/671937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java多线程简明笔记(5)线程礼让 yield

关键字&#xff1a;yield 官方文档就不说了&#xff0c;简单理解&#xff0c;礼让 线程礼让 yield正在执行的线程暂停&#xff0c;不阻塞 示例代码&#xff1a; public class ThreadTest7 implements Runnable{public static void main(String[] args) {ThreadTest7 tnew Th…

在线广告业务流程

一、业务流程 1、注册/登录 广告主登录系统&#xff0c;进行基本的鉴权&#xff0c;角色分配&#xff0c;账户管理。同时为营销参与者包括产品、运营、销售等提供登录、附身功能。 2、代理开户 & 订单合同管理 代理商可代理广告主开户&#xff0c;客户同样统一维护在用户…

帮你解锁 Android 性能优化五大误区和两大疑点!

近年来&#xff0c;社区充斥着关于 Android 性能优化的各种误区&#xff0c;本文本着误区终结者的精神&#xff0c;使用具体的性能检测工具&#xff0c;结合真实案例仔细分析这些情况&#xff0c;并对比它们的测试结果&#xff0c;也会聚焦 Android 开发者平时在编码过程的实际…

【CV 向】OpenCV 图形绘制指南

文章目录 引言1. 创建画布2. 绘制线段3. 绘制矩形4. 绘制圆5. 绘制椭圆6. 绘制多边形7. 绘制字体结论 引言 Python OpenCV 是一个功能强大的计算机视觉库&#xff0c;除了图像处理和计算机视觉任务外&#xff0c;它还提供了丰富的功能来绘制各种图形。无论是在计算机视觉应用中…

基于ChatGPT的端到端语音聊天机器人项目实战(三)

企业级ChatGPT开发入门实战 第1课 基于ChatGPT的端到端语音聊天机器人项目实战 Gavin老师:NLP_Matrix_Space 1.4 使用FastAPI构建语音聊天机器人后端实战 在后端代码(backend)中调用了OpenAI API及其他的服务,如图1-10所示。 图1- 10 后端代码调用OpenAI API openai_requ…

一条SQL语句的前世今生

文章目录 MySQL 基础架构分析语句分析查询语句更新语句 总结 本篇文章会分析下一个 SQL 语句在 MySQL 中的执行流程&#xff0c;包括 SQL 的查询在 MySQL 内部会怎么流转&#xff0c;SQL 语句的更新是怎么完成的。 MySQL 基础架构分析 下图是 MySQL 的一个简要架构图&#xff…

Move Dev Meetup杭州线下交流会圆满结束

北京时间6月17日下午2点到6点&#xff0c;由MoveFuns DAO 、MoveBit、ComingChat、杭州时戳微蓝信息咨询有限公司和WEB3CN联合举办的Move开发者线下交流会圆满结束。本次交流会吸引了110余名Move开发者报名&#xff0c;其中超过60位参会者成功来到现场参与了线下交流。 第一位分…

Python在不同领域中的应用

Python 是一种功能强大且易于使用的编程语言&#xff0c;因此在各个领域都有广泛的应用。以下是 Python 在不同领域中的应用&#xff1a; 数据科学&#xff1a;Python 是数据科学家和机器学习专家的首选工具之一。它有成熟的数据分析库和工具包&#xff0c;如 Pandas、NumPy、S…

逻辑回归模型的简介及python代码实现

文章目录 1 逻辑回归简介1.1 回归步骤1.2 逻辑回归与多重线性回归 2 逻辑回归模型算法原理2.1 逻辑回归模型的数学原理2.2 逻辑回归模型的代码实现2.3 逻辑回归模型的深入理解2.4 多分类逻辑回归模型演示 总结 1 逻辑回归简介 逻辑回归是分类当中极为常用的手段&#xff0c;它…

(六)关于Linux中服务器磁盘爆满问题的几个解决方案

文章目录 一、现象二、现象产生的原因三、问题的定位过程1.检查业务文件存放位置是否合理2.检查服务器系统盘空间是否分配过小3.对服务器磁盘占用情况进行分析4.若以上方案都解决不了内存占用过高问题&#xff0c;则考虑是否是服务器中存在其他容器产生了一些文件&#xff0c;比…

【使用Hystrix实现服务容错和熔断】—— 每天一点小知识

&#x1f4a7; 使用 H y s t r i x 实现服务容错和熔断 \color{#FF1493}{使用Hystrix实现服务容错和熔断} 使用Hystrix实现服务容错和熔断&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390…

进程、线程、协程?go中的协程原理详解 - 协程是什么?为什么要有协程?

1. 为什么要有协程&#xff1f; 我们使用工厂来表示计算机&#xff0c;而内存空间就相当于工厂的地皮&#xff0c;那么我们可以认为进程就是工厂中的厂房&#xff0c;它占据了工厂的地皮。&#xff08;进程是分配资源的最小单位&#xff09; 线程是什么呢&#xff1f;我们继续…

电商运营个人简历范文

电商运营个人简历篇1 个人信息 某某x 性 别&#xff1a; 男 婚姻状况&#xff1a; 已婚 出生年月&#xff1a; 1981年1月 民 族&#xff1a; 汉族 身 高&#xff1a; 175cm 最高学历&#xff1a; 大专 体 重&#xff1a; 70公斤 计算机能力&#xff1a; 高级程序员 技术职称&am…

MySQL的IF(exp1, exp2, exp3)、IFNULL(exp1, exp2)、ISNULL(exp)函数的用法

本章主要是讲解一下mysql的常用方法if()和ifnull()的使用 1、ISNULL(exp) 该函数的作用是判断表达式exp是否为null&#xff0c;如果是null&#xff0c;则返回1&#xff0c;否则返回0 案例代码&#xff1a;以上两条语句的查询结果为分别为1和0 select ISNULL(null) from dual…

管理类联考——逻辑——技巧篇——形式逻辑——考官出题套路

文章目录 考官出题套路一&#xff1a;代入逻辑推命题真假1. 考官出题套路特征&#xff1a;2. 核心考点&#xff1a; 考官出题套路二&#xff1a;判断逻辑真假1. 考点概述&#xff1a;2. 快速秒杀选项技巧&#xff1a; 考官出题套路三&#xff1a;推矛盾和“两难推理”1. 考点概…

一句话木马(绝对入门,实战环境安装+测试)主要是记录我的第一次一句话木马过程,复现40分钟左右。

首先介绍一下&#xff0c;搭建在本地的靶场&#xff0c;可以做渗透和注入的测试&#xff0c;真是好东西。 安装教程我推荐一篇博客&#xff0c;真的写的很好&#xff0c;我全程坐下来非常流畅非常流畅&#xff0c;csdn少见的高质量博客 DVWA下载安装_PisaYu的博客-CSDN博客 膜…

MFC第五天 Unicode软件开发 MFC框架构成与封装类原理

文章目录 Unicode软件开发以Unicode为字符集的记事本软件开发 MFC框架构成与封装类原理示例代码如下&#xff1a; Unicode软件开发 Unicode软件开发时需要遵循以下规则&#xff1a;使用中可尽量使用自适应版本。 Unicode软件开发&#xff1a; a)微软的软件工程现在默认使用Uni…

【MYSQL篇】mysql中相关锁和MVCC详解

文章目录 前言MVCC1、第一个事务2、第二个事务3、第三个事务4、第四个事务5、第五个事务 InnoDB 常见的几种锁机制共享锁排它锁意向锁记录锁间隙锁临键锁 小结 前言 数据库的锁是在多线程高并发的情况下用来保证数据稳定性和一致性的一种机制。MySQL 根据底层存储引擎的不同&a…

OpenGL

需要继承的两个类 #include <QOpenGLWidget> #include <QOpenGLFunctions_3_3_Core> class OpenGLWidget : public QOpenGLWidget,public QOpenGLFunctions_3_3_Core { Q_OBJECT public: explicit OpenGLWidget(QWidget *parent nullptr); virtual vo…

如何看待调查称半数年轻人存款不足10万?

文章目录 一、目前的存款在哪一个区间&#xff1f;你觉得存款难吗&#xff1f;2.1 自己的状态2.2 对理财的看法和态度 二、谈谈我为存款做出过哪些努力&#xff1f;三、除了个人因素外&#xff0c;有哪些因素影响到了年轻人的存款能力和存款意愿&#xff1f;四、要攒够多少存款…