Java 异步编程——Java内置线程调度器(Executor 框架)

文章目录

    • Java多线程的两级调度模型
    • Executor 框架
      • Executor 框架的组成概念
      • Executor 框架中任务执行的两个阶段:任务提交和任务执行

在 Java1.5 以前,开发者必须手动实现自己的线程池;从 Java1.5 开始,Java 内部提供了线程池。

在Java 异步编程——既然可以手动创建线程,为什么还要使用线程池这篇文章提到过Java线程会映射到操作系统本地线程上,使得 Java 线程能够在操作系统层面上得到执行,在大多数情况下,每个 Java 线程都会对应一个本地操作系统线程,但具体的映射方式和数量由 JVM 和操作系统的实现决定。这里Java线程与操作系统本地线程两个层次的线程模型,存在两个层次的调度机制。所以在介绍Java内置线程池之前,先一起了解Java多线程的两级调度模型。

Java多线程的两级调度模型

Java 多线程的两级调度模型是指在 Java 多线程程序中,存在两个层次的调度机制:用户级调度和操作系统级调度。

用户级调度:(用户级线程 ULT)

  • 用户级线程由 Java 虚拟机(JVM)自身管理,不需要操作系统内核的介入。
  • 用户级调度是指由应用程序自身实现的线程调度机制。在这一级别上,应用程序可以使用 Java 提供的并发编程工具(例如 Executor 框架、线程池等用户级的调度器)来管理和调度线程。应用程序根据自身的需求,将任务分配给可用的Java线程,决定线程的执行顺序和优先级等。Java 线程是由 JVM 提供的线程管理机制来调度执行的,所以用户级调度是在 Java 层面上进行的,不涉及操作系统的线程调度。

操作系统级调度:(内核级线程 KLT)

  • 内核级线程是由操作系统内核管理的真正的系统级线程。
  • 操作系统级调度是指操作系统内核对线程进行的调度和执行。在这一级别上,操作系统负责将Java线程映射到底层的操作系统线程(本地操作系统线程),并分配 CPU 进行实际的计算。操作系统线程模型可以利用操作系统提供的并发机制和资源管理功能,如线程调度器、线程优先级、时间片轮转等,以确保公平性和资源利用的最大化。

两级调度模型的优势:

Java 多线程的两级调度模型的优势在于结合了用户级调度和操作系统级调度的特点:

  • 用户级调度使应用程序具有灵活性和可控性,可以根据具体需求自主管理和调度线程。Java 线程模型提供了高级的并发编程抽象,使得开发者可以更方便地编写并发程序,从而可以根据任务的特点和优先级,自主决定线程的创建、启动、休眠、唤醒等操作,实现更细粒度的线程调度。
  • 操作系统级调度利用了底层操作系统的调度机制和资源管理功能,确保线程的公平性和高效利用。操作系统级调度可以根据底层硬件的特点和系统负载情况,智能地分配线程执行的资源,提高性能和资源利用率。
  • 用户级线程的创建、切换和调度都发生在 JVM 内部,效率较高;内核级线程的创建、切换和调度需要操作系统内核的参与,开销相对较大。
  • 当一个用户级线程阻塞(如进行 I/O 操作)时,如果是单线程,整个进程也会被阻塞,无法利用其他可用的 CPU 资源;在多线程情况下,JVM 会将其挂起,并将另一个可运行的用户级线程映射到内核级线程上继续执行,避免了进程级的阻塞,提高整体并发性能。
    在这里插入图片描述

Executor 框架

上面讲述了 Java 多线程的两级调度模型,对于Java编程,我们只需关注用户级调度(Java线程)。Java 多线程管理是由在 Java 内置的 Executor 框架下完成的。Executor 框架是 Java 5 引入的一套用于异步任务执行的API,提供了一种简化线程管理和任务执行的方式,将任务的提交和执行分离开来。

Executor 框架的组成概念

首先简单介绍一下 Executor 框架的组成概念。

  1. 任务:任务指的是被异步执行的工作单元(工作代码),任务需要实现接口:Runable 接口 or Callable 接口;
    • Runable 接口:只定义了一个没有返回值的 run() 方法,用于封装需要执行的任务代码
    • Callable 接口:只定义了一个带返回值的 call() 方法,用于封装需要执行的任务代码并返回结果。
  2. 任务执行器:负责实际执行任务的组件,包括任务执行机制的核心接口 Executor,以及继承自 Executor 的 ExecutorService 接口。Executor 框架有几个关键类实现了 ExecutorService 接口:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor、ForkJoinPool;
    • Executor 接口:任务执行器的核心接口,定义了一个简单的 execute(Runnable command) 方法用于异步执行任务。
    • ExecutorService 接口:继承自 Executor 接口,添加了更丰富的任务提交和生命周期管理方法,如 submit(Callable task)、submit(Runnable task) 、shutdown() 等。
      • submit(Callable task)方法返回一个Future对象,可以通过Future对象的get()方法获取任务的返回值。
      • submit(Runnable task)方法返回一个Future对象,但该Future对象的get()方法返回null。
    • ThreadPoolExecutor 类:是 Executor 框架中最重要的任务执行器实现,实现了 ExecutorService 接口。提供了一个可配置的线程池,用于执行异步任务。
    • ScheduledThreadPoolExecutor 类:继承自 ThreadPoolExecutor,额外提供了定时和周期性任务执行的功能。
    • ForkJoinPool 类:提供了一个支持工作窃取算法的线程池实现,适用于分治类型的并行计算。
  3. 获取任务执行结果:包括 Future 接口和实现 Future 接口的 FutureTask 类、ForkJoinTask 类。
    • Future 接口:获取任务执行结果核心接口,定义获取异步任务执行结果的相关方法。当提交 Callable 任务时,会返回一个 Future 对象。通过这个 Future 对象对应方法可以查询任务的执行状态、获取执行结果、取消任务等。还提供了丰富的 API,如 get()、isDone()、cancel() 等,用于管理异步任务的生命周期。
    • FutureTask 类:Future 接口的一个具体实现类。
    • ForkJoinTask 抽象类:继承自 Future 接口,用于支持 ForkJoinPool 中任务的异步计算。ForkJoinTask 提供了 fork() 和 join() 方法,用于将任务拆分并行执行,然后合并结果。

Executor框架的使用示意图:
在这里插入图片描述

Executor 框架中任务执行的两个阶段:任务提交和任务执行

前面分析 Executor 框架时,Executor 接口只有一个执行任务的方法 execute(),ExecutorService 接口增加了一个提交任务的方法 submit()。从系统层面来讲,这两个方法都是任务提交方法,如果任务只是简单的异步执行,不需要返回值或处理异常,可以直接使用 execute() 进行任务提交;如果任务需要返回值或处理异常,则使用 submit() 方法进行提交。再深入代码研究,submit() 方法最终还是会调到 execute() 实现最终的任务提交,可以理解 submit() 在任务提交前进行了一系列处理,使其能够在任务执行完后获得返回值或处理异常。submit() 方法在 AbstractExecutorService 抽象类中由默认实现,execute() 在 ThreadPoolExecutor 类中实现。最后,Executor 根据线程池的配置和调度策略分配线程执行任务。

ThreadPoolExecutor 继承 AbstractExecutorService 实现 ExecutorService。

两个阶段:

  • 任务提交阶段:在任务提交阶段,应用程序通过调用 Executor 类的 execute(Runnable command) 或 ExecutorService 的 submit(Callable task) 方法将任务提交给 Executor。在这个阶段,任务被封装成一个任务对象,可以是 Runnable 对象或 Callable 对象,并被添加到任务队列中等待执行。当任务被提交后,Executor 实现类会对任务进行排队和调度。

  • 任务执行阶段:在任务执行阶段,Executor 根据线程池的配置和调度策略,从它管理的线程池中分配一个空闲线程来执行任务。线程从任务队列中取出任务,并调用任务的 run() 或 call() 方法来执行任务。任务执行完成后,线程会被返回到线程池中,等待被再次分配执行新的任务。

任务提交阶段可以根据应用程序的需要灵活地控制任务的产生和提交,任务执行阶段则由 Executor 框架负责管理和调度线程的执行,从而实现更高的并发性能和更好地利用系统资源。这种将任务的提交和执行解耦,开发者只需关注任务的定义和提交,而无需过多地关注任务的执行细节。可以根据实际需求来调整任务队列的容量、任务队列的类型、拒绝策略以及线程池的配置(线程池的大小)等参数。

new Thread 创建线程与任务执行:

在学习 Java 线程时,都曾看到过Java创建多线程的三种方式:继承Thread类、实现Runnable接口以及实现Callable接口。经过前面的分析,Runnable接口和Callable接口都是代表任务接口,封装要执行的任务代码。实现Runnable接口以及实现Callable接口两种方式严格来说不是创建线程,而真正创建多线程的方式只有一种:继承Thread类,只有通过 new Thread().start() 这种方式才能真正的创建Java线程并映射到操作系统的内核线程上。

其实上述三种方式更好的理解是创建并提交任务的三种方式,相比之下:

  1. 解耦任务逻辑:直接创建 Thread 对象会将任务逻辑和线程管理耦合在一起,不利于代码的维护和扩展;而使用 Runnable 或 Callable 接口可以将任务的逻辑与线程的管理分离开来,使得代码更加模块化和可复用。
  2. 线程池管理:经过前面的分析,使用 Runnable 或 Callable 接口可以将任务提交给线程池(ExecutorService)进行管理和执行,多个任务可以方便复用多个线程执行处理,从而避免频繁创建和销毁线程的开销;而直接创建 Thread 对象,需要自己管理线程的生命周期,比较麻烦。
  3. 任务结果和异常处理:Callable接口可以返回任务的执行结果和异常处理,Runnable接口虽然不能返回结果,但也可以通过Future对象获取任务的执行状态和异常信息,而直接创建Thread对象,需要自己处理任务的返回值和异常。

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

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

相关文章

Python代码:十九、列表的长度

1、题目 描述: 牛牛学会了使用list函数与split函数将输入的连续字符串封装成列表,你能够帮他使用len函数统计一些公输入了多少字符串,列表中有多少元素吗? 输入描述: 输入一行多个字符串,字符串之间通过…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt + uniapp 驾校预约平台 的设计与实现

一.项目介绍 系统角色:管理员、教练、学员 小程序(仅限于学员注册、登录): 查看管理员发布的公告信息 查看管理员发布的驾校信息 查看所有教练信息、预约(需教练审核)、评论、收藏喜欢的教练 查看管理员发布的考试信息、预约考试(需管理…

流媒体内网穿透/组网/视频协议转换EasyNTS上云网关如何更改密码?

EasyNTS上云网关的主要作用是解决异地视频共享/组网/上云的需求,网页对域名进行添加映射时,添加成功后会生成一个外网访问地址,在浏览器中输入外网访问地址,即可查看内网应用。无需开放端口,EasyNTS上云网关平台会向Ea…

【linux】深入了解线程池:基本概念与代码实例(C++)

文章目录 1. 前言1.1 概念1.2 应用场景1.3 线程池的种类1.4 线程池的通常组成 2. 代码示例2.1 log.hpp2.2 lockGuard.hpp① pthread_mutex_t 2.3 Task.hpp2.4 thread.hpp2.5 threadPool.hpp① 基本框架② 成员变量③ 构造函数④ 其余功能函数: main.cc结果演示 完整…

动态规划-似包非包问题

组合总和 Ⅳ(377) 题目描述: 状态表示: 我们看到这题发现有一个限制条件就是目标整数target并且此时数组中的数字是可以重复选择的,这时候不难联想到前面学习的完全背包问题,这题好像符合完全背包问题的…

关于linux磁盘告警问题

案例:我们在执行df命令时,查看到磁盘利用率很高,但是到相对应的目录执行du -sh *来找大文件时进行删除时,发现各个目录相加并不大,如下图: 使用df命令查看到根(/)目录使用到33G,而du命令显示只使…

FuTalk设计周刊-Vol.050

#AI漫谈 热点捕手 1.Canva 宣布收购 Affinity 创意套件 平面设计平台 Canva 于 3 月 26 日宣布收购知名设计软件 Affinity 以“迎战”Adobe,不过此后许多设计师开始担心原本采用“永久授权”付费方案的 Affinity 系列软件是否会转为订阅制,而目前 Canv…

Android Studio开发之路(十四)自定义Titlebar以及设置顶部状态栏颜色

一、描述 项目需求,我要做一个下图这样的titlebar,包括一个返回按钮,一个关闭按钮,一个文本框。默认的titlebar按钮设计不太满足我的需求,于是我打算自定义一个titlebar组件,应用到我的每一个页面 二、titlebar组件设…

【NumPy】关于numpy.searchsorted()函数,看这一篇文章就够了

🧑 博主简介:阿里巴巴嵌入式技术专家,深耕嵌入式人工智能领域,具备多年的嵌入式硬件产品研发管理经验。 📒 博客介绍:分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向…

【Qt】数据库(一)SQLITE创建、增删查改

填坑1&#xff1a;如何连续插入 汇总SQlite语句 创建表格&#xff1a;create table <table_name> (f1 type1, f2 type2,…); 增&#xff1a;insert into <table_name> values (value1, value2,…); 改&#xff1a;update <table_name> set <f1value1>,…

数据结构第二篇【关于java线性表(顺序表)的基本操作】

【关于java线性表&#xff08;顺序表&#xff09;的基本操作】 线性表是什么&#xff1f;&#x1f435;&#x1f412;&#x1f98d;顺序表的定义&#x1f9a7;&#x1f436;&#x1f435;创建顺序表新增元素,默认在数组最后新增在 pos 位置新增元素判定是否包含某个元素查找某个…

使用高性能NIO框架netty实现IM集群对聊方案

文章目录 前言技术积累什么是nettynetty如何实现IM如何实现IM集群 实战演示基础配置netty搭建IM集群redis发布订阅 实战测试 前言 在前面的博文中我们分享了原生websoket集群搭建&#xff0c;也用redis 发布订阅实现了集群消息正常有序分发。但是有不少同学希望风向一期netty实…

Json差异比较

json差异比较 如何比较两个json的差异 代码实现 导入依赖 <dependency><groupId>cn.xiaoandcai</groupId><artifactId>json-diff</artifactId><!-- 旧版本可能存在某些缺陷。版本请以maven仓库最版为准。 --><version>4.1.3-RC1-R…

问题记录_stm32“No target connected“

问题描述&#xff1a; 基于HAL库和stm32cubeMX生成的代码&#xff0c;烧录时出现如下报错窗口&#xff1a; 问题原因&#xff1a; stm32cubeMX生成代码时关闭了SWJ调试功能 解决方法&#xff1a; 在项目中找到__HAL_AFIO_REMAP_SWJ_DISABLE();并注释掉 然后短按复位键的…

AI大模型是如何测试效果的?

AI大模型的测试和评估是一个复杂的过程&#xff0c;通常包括多个方面的考量&#xff0c;因此对大模型的测试也称为多度测试。 可以简单概括为以下几个方面&#xff1a; 基准测试&#xff08;Benchmarking&#xff09;&#xff1a;使用标准数据集和任务评估模型性能&#xff0c…

c语言从入门到函数速成(完结篇)

哈喽&#xff0c;小伙伴们大家好呀&#xff0c;本篇文章是这个系列的完结篇&#xff0c;希望大家看完后能有所收获哦 首先能看到这里的同学&#xff0c;一定也是自觉性比较强的了&#xff0c;我会在文章末尾给大家发点小福利 那么&#xff0c;我们先来通过数学中的函数来引入一…

基于python的k-means聚类分析算法,对文本、数据等进行聚类,有轮廓系数和手肘法检验

K-means算法是一种常见的聚类算法&#xff0c;用于将数据点分成不同的组&#xff08;簇&#xff09;&#xff0c;使同一组内的数据点彼此相似&#xff0c;不同组之间的数据点相对较远。以下是K-means算法的基本工作原理和步骤&#xff1a; 工作原理&#xff1a; 初始化&#x…

Elasticsearch之入门与安装

Elaticsearch&#xff0c;简称为es&#xff0c; es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b;本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理PB级别的数据。es也使用Java开发并使用Lucene作为其核心来…

探索python循环逻辑的魅力:从无限到有限

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;循环逻辑的初步认识 二、无限循环&#xff1a;持续运转的引擎 三、有…

通过 PW6606 快充电压诱骗芯片,了解 USB-A 与 USB-C 快充协议

充电器一般分两种&#xff1a; 1&#xff0c; A 口充电器&#xff0c;就是我们常见的 USB 口&#xff0c;如下图&#xff0c;这种通用快充协议叫&#xff1a; QC3.0,QC2.0 快充&#xff0c;是属于快充刚开始的充电协议&#xff0c;支持 5V,9V,12V 和 20V 电压输出充电器&#x…