(14)Reactor调度器与线程模型——响应式Spring的道法术器

Reactor响应式编程系列(一)- 初识Reactor3 Reactor响应式编程系列(一)- 初识Reactor前言一. 响应式编程二. Flux 和 Mono2.1 创建序列2.2 subscribe订阅2.3 背压和重塑请求三. 可编程式方式创建序列3.1 Generate3.2 Create3.3 包装同步阻塞 前言 临近毕业,去新公司学习,公司主要以Reactor3来做响应式编程,因此准备借此机会对响应式编程进行一个系统的学习。也希望大家能多多交流,毕竟网上对这一块内容的相关博客还是比较少的。 目前准备对Reactor的这么几个方面进行归纳,后期的博客内 阅读详情

本系列其他文章见:《响应式Spring的道法术器》。
前情提要:Reactor3快速上手 | 深入理解响应式流规范 | Reactor3自定义数据流
本文测试源码

2.4 调度器与线程模型

在1.3.2节简单介绍了不同类型的调度器Scheduler,以及如何使用publishOn和subscribeOn切换不同的线程执行环境。

下边使用一个简单的例子再回忆一下:

    @Test
    public void testScheduling() {
        Flux.range(0, 10)
//                .log()
                .publishOn(Schedulers.newParallel("myParallel"))
//                .log()
                .subscribeOn(Schedulers.newElastic("myElastic"))
                .log()
                .blockLast();
    }
  1. 只保留这个log()的话,可以看到,源头数据流是执行在myElastic-x线程上的;
  2. 只保留这个log()的话,可以看到,publishOn之后数据流是执行在myParallel-x线程上的;
  3. 只保留这个log()的话,可以看到,subscribeOn之后数据流依然是执行在myParallel-x线程上的。

通过以上三个log()的输出,可以发现,对于如下图所示的操作链:

  • publishOn会影响链中其后的操作符,比如第一个publishOn调整调度器为elastic,则filter的处理操作是在弹性线程池中执行的;同理,flatMap是执行在固定大小的parallel线程池中的;
  • subscribeOn无论出现在什么位置,都只影响源头的执行环境,也就是range方法是执行在单线程中的,直至被第一个publishOn切换调度器之前,所以range后的map也在单线程中执行。

这一节我们了解一下它的实现机制。

2.4.1 调度器

调度器相当于Reactor中的ExecutorService,不同的调度器定义不同的线程执行环境。Schedulers工具类提供的静态方法可搭建不同的线程执行环境。

Schedulers类已经预先创建了几种常用的不同线程池模型的调度器:使用single()、elastic()和parallel()方法创建的调度器可以分别使用内置的单线程、弹性线程池和固定大小线程池。如果想创建新的调度器,可以使用newSingle()、newElastic()和newParallel()方法。这些方法都是返回一个Scheduler的具体实现。

看一下Scheduler都有哪些行为:

public interface Scheduler extends Disposable {
    // 调度执行Runnable任务task。
	Disposable schedule(Runnable task);
	// 延迟一段指定的时间后执行。
	Disposable schedule(Runnable task, long delay, TimeUnit unit);
	// 周期性地执行任务。
	Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit);
	// 创建一个工作线程。
	Worker createWorker();
	// 启动调度器
	void start();
	// 以下两个方法可以暂时忽略
	void dispose();
	long now(TimeUnit unit)
	
	// 一个Worker代表调度器可调度的一个工作线程,在一个Worker内,遵循FIFO(先进先出)的任务执行策略
	interface Worker extends Disposable {
    	// 调度执行Runnable任务task。
    	Disposable schedule(Runnable task);
    	// 延迟一段指定的时间后执行。
    	Disposable schedule(Runnable task, long delay, TimeUnit unit);
    	// 周期性地执行任务。
    	Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit);
	}
}

如图所示,Scheduler是领导,Worker是员工,每个Scheduler手中有若干Worker。接到任务后,Scheduler负责分派,Worker负责干活。

在Scheduler中,每个Worker都是一个ScheduledExecutorService,或一个包装了ScheduledExecutorService的对象。所以,Scheduler拥有的并不是线程池,而是一个自行维护的ScheduledExecutorService池。

所谓“自行维护”,主要有三点:

  1. 可供调遣的Worker。比如Schedulers.newParallel()返回的ParallelScheduler,其内维护的是一个固定大小的ScheduledExecutorService[]数组;而ElasticScheduler由一个ExecutorService的Queue来维护。
  2. 任务分派策略。ElasticScheduler和ParallelScheduler都有一个pick()方法,用来选出合适的Worker。
  3. 对于要处理的任务,包装为Callable,从而可以异步地返回一个Future给调用者。

2.4.2 切换执行环境

再回到publishOn和subscribeOn方法。

在Reactor中,对于数据流的处理,实际上是一系列方法调用和基于事件的回调,包括subscribe、onSubscribe、request,以及onNext、onError、onComplete。拿出2.1节的图帮助理解:

当调用.subscribe()方法时,会形成从上游向下游的数据流,数据流中的元素通过onNext* (onError|onComplete)携带“顺流而下”。同时,Reactor使用者看不到的是,还有一条从下游向上游的“订阅链”,request就是沿着这个链向上反馈需求的。

publishOn方法能够将onNext、onError、onComplete调度到给定的Scheduler的Worker上执行。所以如上图场景中,再.map和.filter中间增加一个publisheOn(Schedulers.elastic())的话,.filter操作的onNext的过滤处理将会执行在ElasticScheduler的某个Worker上。

subscribeOn方法能够将subscribe(会调用onSubscribe)、request调度到给定的Scheduler的Worker上执行。所以在任何位置增加一个subscribeOn(Schedulers.elastic())的话,都会借助自下而上的订阅链,通过subscribe()方法,将线程执行环境传递到“源头”,从而Flux.just会执行在ElasticScheduler上。继而影响到其后的操作符,直至遇到publishOn改变了执行环境。

此外,有些操作符本身会需要调度器来进行多线程的处理,当你不明确指定调度器的时候,那些操作符会自行使用内置的单例调度器来执行。例如,Flux.delayElements(Duration) 使用的是 Schedulers.parallel()调度器对象:

    @Test
    public void testDelayElements() {
        Flux.range(0, 10)
                .delayElements(Duration.ofMillis(10))
                .log()
                .blockLast();
    }

从输出可以看到onNext运行在不同的线程上:

[ INFO] (main) onSubscribe(FluxConcatMap.ConcatMapImmediate)
[ INFO] (main) request(unbounded)
[ INFO] (parallel-1) onNext(0)
[ INFO] (parallel-2) onNext(1)
[ INFO] (parallel-3) onNext(2)
[ INFO] (parallel-4) onNext(3)
...

2.4.3 为数据流配置Context

在Reactor中,基于Scheduler的线程调度确实非常简单好用,但是还有个问题需要解决。

我们以往在编写多线程的代码时,如果涉及到只在线程内部使用的值,可能会使用ThreadLocal进行包装。

但是在响应式编程中,由于线程环境经常发生变化,这一用法就失去作用了,并且甚至带来bug。比如,使用 Logback 的 MDC 来存储日志关联的 ID 就属于这种情况。

自从版本 3.1.0,Reactor 引入了一个类似于 ThreadLocal 的高级功能:Context。它作用于一个 Flux 或一个 Mono 上,而不是应用于一个线程(Thread)。也就是其生命周期伴随整个数据流,而不是线程。

相对来说,用户使用Context并不多,对此感兴趣或有此需求的话,请看我翻译的相关文档,可以对Reactor内部实现尤其是Subscription有更深的理解。

2.4.4 并行执行

如今多核架构已然普及,能够方便的进行并行处理是很重要的。

对于一些能够在一个线程中顺序处理的任务,即使调度到ParallelScheduler上,通常也只由一个Worker来执行,比如:

    @Test
    public void testParallelFlux() throws InterruptedException {
        Flux.range(1, 10)
                .publishOn(Schedulers.parallel())
                .log().subscribe();
        TimeUnit.MILLISECONDS.sleep(10);
    }

输出如下:

[ INFO] (main) | onSubscribe([Fuseable] FluxPublishOn.PublishOnSubscriber)
[ INFO] (main) | request(unbounded)
[ INFO] (parallel-1) | onNext(1)
[ INFO] (parallel-1) | onNext(2)
[ INFO] (parallel-1) | onNext(3)
[ INFO] (parallel-1) | onNext(4)
[ INFO] (parallel-1) | onNext(5)
[ INFO] (parallel-1) | onNext(6)
[ INFO] (parallel-1) | onNext(7)
[ INFO] (parallel-1) | onNext(8)
[ INFO] (parallel-1) | onNext(9)
[ INFO] (parallel-1) | onNext(10)
[ INFO] (parallel-1) | onComplete()

有时候,我们确实需要一些任务能够“均匀”分布在不同的工作线程上执行,这时候就需要用到ParallelFlux。

你可以对任何Flux使用parallel()操作符来得到一个ParallelFlux。不过这个操作符本身并不会进行并行处理,而只是将负载划分到多个执行“轨道”上(默认情况下,轨道个数与CPU核数相等)。

为了配置ParallelFlux如何并行地执行每一个轨道,需要使用runOn(Scheduler),这里,Schedulers.parallel() 是比较推荐的专门用于并行处理的调度器。

    @Test
    public void testParallelFlux() throws InterruptedException {
        Flux.range(1, 10)
                .parallel(2)
                .runOn(Schedulers.parallel())
//                .publishOn(Schedulers.parallel())
                .log()
                .subscribe();

        TimeUnit.MILLISECONDS.sleep(10);
    }

输出如下:

[ INFO] (main) onSubscribe([Fuseable] FluxPublishOn.PublishOnSubscriber)
[ INFO] (main) request(unbounded)
[ INFO] (main) onSubscribe([Fuseable] FluxPublishOn.PublishOnSubscriber)
[ INFO] (main) request(unbounded)
[ INFO] (parallel-1) onNext(1)
[ INFO] (parallel-2) onNext(2)
[ INFO] (parallel-1) onNext(3)
[ INFO] (parallel-2) onNext(4)
[ INFO] (parallel-1) onNext(5)
[ INFO] (parallel-2) onNext(6)
[ INFO] (parallel-1) onNext(7)
[ INFO] (parallel-2) onNext(8)
[ INFO] (parallel-1) onNext(9)
[ INFO] (parallel-2) onNext(10)
[ INFO] (parallel-1) onComplete()
[ INFO] (parallel-2) onComplete()

可以看到,各个元素的onNext “均匀”分布执行在两个线程上,最后每个线程上有独立的onComplete事件,这与publishOn调度到ParallelScheduler上的情况是不同的。

(4)Reactor 3快速上手——响应式Spring的道法术器 本系列其他文章见:《响应式Spring的道法术器》。 前情提要:响应式流 | lambda与函数式 1.3.2 Reactor Reactor与Spring是兄弟项目,侧重于Server端的响应式编程,主要 artifact 是 reactor-core,这是一个基于 Java 8 的实现了响应式流规范 (Reactive Streams specification)的响应... 阅读详情

相关推荐

2026最新Java八股文:面试必备知识体系与实战技巧

Java作为企业级开发的主流语言,其知识体系涵盖从基础语法到JVM原理的多个层次。理解Java内存模型、并发编程机制等核心原理,是构建高性能应用的基础。随着云原生和响应式编程的普及,Java技术栈不断演进,掌握GraalVM、虚拟线程等新特性成为开发者竞争力的关键。本资源采用四维知识图谱架构,系统梳理Java 21+特性、Spring 6.x框架原理等热点技术,通过理论定义、实现原理、实践验证的三层学习法,帮助开发者快速构建完整的Java知识体系,应对技术面试挑战。

weixin_33766168的博客 442

spring webFlux

一个大神对flux与mvc访问数据库的效率对比报告 1、与传统的Spring MVC相比 webFlux是异步非阻塞线程模型,它能够充分利用多核 CPU 的硬件资源,springMVC使用的是同步的阻塞的线程模型,一个请求对应一个线程。 因为Spring MVC使用的是一个请求一个线程所以,对于单次请求来说Spring MVC的响应时间更短(毕竟你复用线程的过程中是有上下文切换,而且Flux也不是为了加快单次请求而出现的) 但是 因为Flux的线程模型,所以Flux在吞吐量上更有优势,也就是单位时间会处理

songchenglu123的博客 517

【信息科学与工程学】计算机科学与自动化——第一百三十五篇 开发框架篇 101开发框架对比

EJB 事务是“容器替你管到底”的重量级契约,Spring 事务是“你定义规则、容器帮你执行”的轻量级抽象。对比点数学表达含义EJB 事务成本重但标准Spring 本地事务成本轻量Spring JTA 事务成本介于两者之间代理开销Spring AOP 每次调用额外成本NESTED 保存点仅部分回滚时支付XA 2PC分布式强一致代价自调用失效(无代理)Spring AOP 陷阱事务同步回调Spring 特有扩展点对比维度MyBatisVue 3React 18FastAPI。

weixin_49199313的博客 209

响应式编程-Spring Web Flux理论.一

响应式编程-Spring Web Flux理论知识

qq_35040959的博客 1414

【信息科学与工程学】计算机科学与自动化——第八十一篇 Java分布式软件高并发/高可用算法01

4. 消息队列(如Kafka的Topic分区,本质是范围)。3. 若是ACCEPT事件,bossGroup线程接受连接,创建SocketChannel,并将其注册到workerGroup的一个EventLoop的Selector上,关注READ事件。1. 分布式缓存分片(如Redis Cluster)。4. 分布式文件存储(如Ceph CRUSH)。6. 调用栈的模拟(如协程)。3. 服务网格(如Istio)的基础。2. 本地缓存与集中式缓存(如Redis)的一致性维护。3. 静态数据(如配置)缓存。

weixin_49199313的博客 1229

【信息科学与工程学】计算机科学与自动化 第二百零一十一 软件开发模型01

编号类型模型领域模型中的知识点/因素/参数关联知识和法律法规1传统顺序模型瀑布模型 (Waterfall)通用软件开发、硬件紧密耦合系统、政府/军工项目、需求稳定的传统信息系统严格线性阶段:可行性研究→需求分析(SRS,含功能/非功能/接口/约束,使用IEEE 830模板)→系统设计(概要设计HLD:架构、模块划分、数据库ER图;详细设计LLD:伪代码、类图、接口定义)→实现(编码规范、代码审查、静态分析工具SonarQube)→测试(单元测试JUnit、集成测试、系统测试、验收测试UAT,测试计划提前编写

weixin_49199313的博客 149

响应式编程框架Reactor【8】

摘要 本文介绍了Reactor响应式编程的性能优化与工作原理。主要内容包括: 性能优化实践:合理使用调度器、避免阻塞操作、缓存优化、背压处理策略、并发控制及监控指标收集等优化技巧,通过代码示例展示了IO密集型操作、非阻塞替代方案等实现方式。 Reactor执行流程:通过序列图展示了订阅者与发布者之间的交互过程,包括操作链创建、线程调度、背压请求和错误处理机制。 背压机制原理:流程图详细说明了不同背压策略(缓冲、丢弃、报错等)的处理逻辑和条件判断过程。

编程技术圈 1239

Reactor 3中文帮助文档

Reactor3中文帮助文档,帮助初学者了解非阻塞响应式式框架的使用以及原理,帮助了解spring webflux的一些底层实现原理

Java MCP Expert 实战指南:基于官方 MCP Java SDK 与响应式流构建生产级 MCP Server

本指南以 awesome-copilot 仓库中的 [Java MCP Expert 智能体](https://link.gitcode.com/i/902c60b4bd3db3fe651df64788b793da) 为主线,完整梳理其关于构建 Java MCP Server 的核心知识与工程模式:面向 MCP(Model Context Protocol)三大原语(工具 Tool、资源 Reso

gitblog_00161的博客 212

Reactor 3 参考文档

Reactor 3 参考文档

pi-star图形界面安装指南

pi-star图形界面安装指南,MMDVM热点安装,树莓派3B+热点

blueisman的专栏 1909

Java深入解析篇五十之Project Reactor

/ 自定义parallel调度器// 自定义boundedElastic调度器20, // 最大线程数1000, // 队列容量"my-io" // 线程名前缀// 从已有Executor创建// 使用完毕后释放规则说明不要 block()在响应式链中永远不要调用 block()用 defer 包装有副作用的创建用 defer/fromCallable显式错误处理每个外部调用都应有错误处理策略合理选择调度器。

萧瑟余晖的博客 346

VSCode关联VIVADO编辑Verilog

有时候接手别人的代码,或者从网上找的开源代码,每个人风格不一致,对齐缩进也不一样,阅读起来很累。有没有什么方法能快捷地格式化代码呢。下面我们利用VSCode插件来实现这个需求。Vivado是Xilinx公司提供的逻辑开发平台(之一),如果不喜欢Vivado自带的文本编辑器,可以选择第三方的文本编辑器VSCode。

小王在努力...的博客 7150

Spring Boot 3.2 + Loom响应式重构五步法,遗留单体系统72小时内完成零停机灰度上线(含自动化检测脚本)

72小时零停机完成Java项目Loom响应式编程转型指南落地!面向企业级应用场景,详解Spring Boot 3.2+Loom五步重构法、灰度上线与自动化检测脚本。覆盖单体系统升级痛点,兼顾性能与稳定性,值得收藏。

CodeWhim的博客 363

C# 实现文件和字符串的AES加密解密

C# 实现文件和字符串的AES128加密解密

m0_72044573的博客 5935

An工具介绍之摄像头

An工具介绍之摄像头

人生得意须尽欢 6187

Spring Boot 4 与 Kotlin 2.2:原生协程与惯用开发实战

Kotlin 以空安全、协程和函数式特性成为现代 JVM 后端开发的热门选择,但传统 Spring Boot 对 Kotlin 的支持往往停留在语法兼容层面。Spring Boot 4 基于 Spring Framework 7,原生支持 Kotlin 2.2 与 K2 编译器,彻底移除 XML 配置,并深度集成协程、suspend 函数与 HTTP Interface,让 Kotlin 开发者摆脱 Java 思维束缚。本文从 Kotlin 语言核心优势切入,剖析 Spring Boot 4 底层架构变化,

weixin_34007020的博客 507

LVGL字体优化实战:如何将中文字库放到外部SPI Flash并动态加载(节省内部RAM)

本文详细介绍了LVGL在嵌入式UI开发中如何通过外部SPI Flash存储中文字库并动态加载,以节省内部RAM资源。通过对比不同存储介质和字库格式,提供多种实现方案及性能优化技巧,帮助开发者在资源受限设备上实现高效的中文显示。

weixin_28693683的博客 343

【Loom响应式重构黄金窗口期】:JDK21 LTS已发布,你的Java 8/17项目还剩9个月兼容缓冲期!

JDK21 LTS发布,Java项目需在9个月内完成Loom响应式编程转型。本指南提供Java项目Loom响应式编程转型指南与性能调优指南,覆盖线程模型重构、虚拟线程集成及异步流优化,显著提升高并发吞吐与资源利用率。值得收藏。

LearnFlow的博客 196

RK3568平台开发系列讲解(外设篇)CAN的使用

📢 本篇章带大家快速了解CAN的使用。

内核笔记 2352

【即插即用模块】卷积篇 | TGRS 2025 | HLKConv:小核+空洞=大核,感受野一致,但更轻更准!

本文介绍了分层大核卷积模块HLKConv,该模块通过动态大核+空洞卷积实现高效感受野扩展,将超大核卷积分解为小核+空洞卷积(如53×53核用17×17+dilation=3实现),计算量仅为普通大核的1/10。模块采用双分支设计:基础卷积分支捕捉局部细节,大核空洞分支捕捉长距离关联,通过1×1卷积融合。实验表明HLKConv在红外小目标检测中优于传统大核卷积,适用于高分辨率视觉任务。代码实现了不同核尺寸的配置,输入输出特征维度保持不变,计算效率显著提升。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 346

Java Stream流编程核心概念与性能优化实践

Stream流是Java 8引入的函数式数据处理抽象,通过流水线操作实现高效集合处理。其核心原理基于延迟执行和短路优化,能显著提升大数据量场景下的处理效率。在工程实践中,Stream通过parallelStream()支持并行计算,配合filter/map/reduce等操作符实现声明式编程。典型应用场景包括数据清洗、批量处理和实时计算,特别适合与Spring、Kafka等框架集成。性能测试表明,在10万级以上数据规模时,并行流处理速度可达传统循环的4-10倍。开发时需注意操作顺序优化、避免装箱开销和线程安

weixin_33804582的博客 471

Linux内核vmlinuz文件:了解和编程

vmlinuz文件是Linux操作系统中的内核镜像文件。它包含了Linux内核的压缩版本,用于引导和启动操作系统。vmlinuz文件通常是一个zImage格式的压缩内核镜像,其中包含了操作系统所需的核心功能和驱动程序。vmlinuz文件的名称可能会因Linux发行版和版本而有所不同,但它们的作用是相同的。在某些发行版中,vmlinuz文件可能被重命名为vmlinux或bzImage。

PixelDyno的博客 1351

Java深入解析篇五十一之RxJava

RxJava 是在 JVM 上的官方实现,是一种基于可观察序列(Observable Sequence)的异步编程范式。它把"事件"作为一等公民,通过数据源 → 操作符链 → 订阅者的流水线模型,把异步回调、线程切换、错误处理、取消订阅等横切关注点统一抽象。i < 5 &&!i++) {if (!});this.s = s;// 初始拉 64 个process(v);// 处理完一个再要一个});// 此时才发射 2。

萧瑟余晖的博客 362

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SpringAI2.0

💡官方定义Spring AI 项目旨在简化包含人工智能功能的应用开发,避免不必要的复杂性。项目借鉴了 LangChain 和 LlamaIndex 等著名 Python 项目的灵感,但 Spring AI不是这些项目的直接移植。项目创立的信念是:下一波生成式 AI 应用浪潮不仅属于 Python 开发者,而是遍布所有编程语言。将你的企业数据与 API 连接到 AI 模型。Spring AI 提供的抽象(Abstractions)是开发 AI 应用的基础。这些抽象有多种实现,只需极少的代码改动。

qq_52030824的博客 355

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

上一篇: (13)Reactor的backpressure策略——响应式Spring的道法术器
下一篇: (15)Reactor 3 Operators——响应式Spring的道法术器
享学IT
博客等级 码龄12年 1516粉丝 91原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值