Reactor中的Thread和Scheduler
简介
今天我们要介绍的是Reactor中的多线程模型和定时器模型,Reactor之前我们已经介绍过了,它实际上是观察者模式的延伸。
所以从本质上来说,Reactor是和多线程无关的。你可以把它用在多线程或者不用在多线程。
今天将会给大家介绍一下如何在Reactor中使用多线程和定时器模型。
Thread多线程
先看一下之前举的Flux的创建的例子:
Flux<String> flux = Flux.generate(
() -> 0,
(state, sink) -> {
sink.next("3 x " + state + " = " + 3*state);
if (state == 10) sink.complete();
return state + 1;
});
flux.subscribe(System.out::println);
可以看到,不管是Flux generator还是subscriber,他们实际上都是运行在同一个线程中的。
如果我们想让subscribe发生在一个新的线程中,我们需要新启动一个线程,然后在线程内部进行subscribe操作。
Mono<String> mono = Mono.just("hello ");
Thread t = new Thread(() -> mono
.map(msg -> msg + "thread ")
.subscribe(v ->
System.out.println(v + Thread.currentThread().getName())
)
);
t.start();
t.join();
上面的例子中,Mono在主线程中创建,而subscribe发生在新启动的Thread中。
Schedule定时器
很多情况下,我们的publisher是需要定时去调用一些方法,来产生元素的。Reactor提供了一个新的Schedule类来负责定时任务的生成和管理。
Scheduler是一个接口:
public interface Scheduler extends Disposable
它定义了一些定时器中必须要实现的方法:
比如立即执行的:
Disposable schedule(Runnable task);
延时执行的:
default Disposable schedule(Runnable task, long delay, TimeUnit unit)
和定期执行的:
default Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit)
Schedule有一个工具类叫做Schedules,它提供了多个创建Scheduler的方法,它的本质就是对ExecutorService和ScheduledExecutorService进行封装,将其做为Supplier来创建Schedule。
简单点看Schedule就是对ExecutorService的封装。
Schedulers工具类
Schedulers工具类提供了很多个有用的工具类,我们来详细介绍一下:
Schedulers.immediate():
提交的Runnable将会立马在当前线程执行。
Schedulers.single():
使用同一个线程来执行所有的任务。
Schedulers.boundedElastic():
创建一个可重用的线程池,如果线程池中的线程在长时间内都没有被使用,那么将会被回收。boundedElastic会有一个最大的线程个数,一般来说是CPU cores x 10。 如果目前没有可用的worker线程,提交的任务将会被放入队列等待。
Schedulers.parallel():
创建固定个数的工作线程,个数和CPU的核数相关。
Schedulers.fromExecutorService(ExecutorService):
从一个现有的线程池创建Scheduler。
Schedulers.newXXX:
Schedulers提供了很多new开头的方法,来创建各种各样的Scheduler。
我们看一个Schedulers的具体应用,我们可以指定特定的Scheduler来产生元素:
Flux.interval(Duration.ofMillis(300), Schedulers.newSingle("test"))
publishOn 和 subscribeOn
publishOn和subscribeOn主要用来进行切换Scheduler的执行上下文。
先讲一个结论,就是在链式调用中,publishOn可以切换Scheduler,但是subscribeOn并不会起作用。
这是因为真正的publish-subscribe关系只有在subscriber开始subscribe的时候才建立。
下面我们来具体看一下这两个方法的使用情况:
publishOn
publishOn可以在链式调用的过程中,进行publish的切换:
@Test
public void usePublishOn() throws InterruptedException {
Scheduler s = Schedulers.newParallel("parallel-scheduler", 4);
final Flux<String> flux = Flux
.range(1, 2)
.map(i -> 10 + i + ":"+ Thread.currentThread())
.publishOn(s)
.map(i -> "value " + i+":"+ Thread.currentThread());
new Thread(() -> flux.subscribe(System.out::println),"ThreadA").start();
System.out.println(Thread.currentThread());
Thread.sleep(5000);
}
上面我们创建了一个名字为parallel-scheduler的scheduler。
然后创建了一个Flux,Flux先做了一个map操作,然后切换执行上下文到parallel-scheduler,最后右执行了一次map操作。
最后,我们采用一个新的线程来进行subscribe的输出。
先看下输出结果:
Thread[main,5,main]
value 11:Thread[ThreadA,5,main]:Thread[parallel-scheduler-1,5,main]
value 12:Thread[ThreadA,5,main]:Thread[parallel-scheduler-1,5,main]
可以看到,主线程的名字是Thread。Subscriber线程的名字是ThreadA。
那么在publishOn之前,map使用的线程就是ThreadA。 而在publishOn之后,map使用的线程就切换到了parallel-scheduler线程池。
subscribeOn
subscribeOn是用来切换Subscriber的执行上下文,不管subscribeOn出现在调用链的哪个部分,最终都会应用到整个调用链上。
我们看一个例子:
@Test
public void useSubscribeOn() throws InterruptedException {
Scheduler s = Schedulers.newParallel("parallel-scheduler", 4);
final Flux<String> flux = Flux
.range(1, 2)
.map(i -> 10 + i + ":" + Thread.currentThread())
.subscribeOn(s)
.map(i -> "value " + i + ":"+ Thread.currentThread());
new Thread(() -> flux.subscribe(System.out::println), "ThreadA").start();
Thread.sleep(5000);
}
同样的,上面的例子中,我们使用了两个map,然后在两个map中使用了一个subscribeOn用来切换subscribe执行上下文。
看下输出结果:
value 11:Thread[parallel-scheduler-1,5,main]:Thread[parallel-scheduler-1,5,main]
value 12:Thread[parallel-scheduler-1,5,main]:Thread[parallel-scheduler-1,5,main]
可以看到,不管哪个map,都是用的是切换过的parallel-scheduler。
本文的例子learn-reactive
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/reactor-thread-scheduler/
本文来源:flydean的博客
欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
Reactor中的Thread和Scheduler的更多相关文章
- python中的thread
转载自: http://blog.sina.com.cn/s/blog_9f488855010198vn.html 正确与否未验证 python中得thread的一些机制和C/C++不同:在C/C++ ...
- java中继承thread类的其他类的start()方法与run()方法
java中继承thread或者实现runnable接口的类必须重写run()方法. 如果其执行了start()方法,其实就是启动了线程的run()方法. 注意:如果是实现runnable接口的类是没有 ...
- 在Activity中使用Thread导致的内存泄漏
https://github.com/bboyfeiyu/android-tech-frontier/tree/master/issue-7/%E5%9C%A8Activity%E4%B8%AD%E4 ...
- 2018.3.3 多线程中继承Thread 和实现Runnable接口 的比较(通过售票案例来分析)
多线程中继承Thread 和实现Runnable接口 的比较(通过售票案例来分析) 通过Thread来实现 Test.java package com.lanqiao.demo4; public cl ...
- java之线程(线程的创建方式、java中的Thread类、线程的同步、线程的生命周期、线程之间的通信)
CPU:10核 主频100MHz 1核 主频 3GHz 那么哪一个CPU比较好呢? CPU核不是越多越好吗?并不一定.主频用于衡量GPU处理速度的快慢,举个例子10头牛运送货物快还是1架飞机运 ...
- Java反应式框架Reactor中的Mono和Flux
1. 前言 最近写关于响应式编程的东西有点多,很多同学反映对Flux和Mono这两个Reactor中的概念有点懵逼.但是目前Java响应式编程中我们对这两个对象的接触又最多,诸如Spring WebF ...
- boost和std中的thread的引用参数
boost 1.60.0 先上代码: #include <boost/thread.hpp> #include <iostream> void add(int &i) ...
- Java中继承thread类与实现Runnable接口的区别
Java中线程的创建有两种方式: 1. 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2. 通过实现Runnable接口,实例化Thread类 在实际应用中, ...
- Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别
1. Java中线程的创建有两种方式: (1)通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中. (2)通过实现Runnable接口,实例化Thread类. 2. ...
随机推荐
- vue实现语音播报功能
1,创建一个js文件 (voicePrompt.js) function voicePrompt (text){ new Audio('http://tts.baidu.com/text2audio? ...
- RHSA-2017:2299-中危: NetworkManager 和 libnl3 安全和BUG修复更新(本地提权、代码执行)
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 修复命令: 使用root账号登陆She ...
- GC调优-XX:PrintGCDetails深度解析
查看程序运行GC的运行情况 资源充足的GC情况 新生代 老年代 元空间 因为现在资源充足没有发生GC *案例:将JVM初始化内存与最大内存(防止内存抖动,反复GC)调至10m,new一个50m的数组对 ...
- select函数详解(转)
Select函数在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect. accept.recv或recvfrom这样的阻塞 ...
- 多测师讲解rf _基本使用002_高级讲师肖sir
在你安装好RF-ride之后,桌面就会生成一个RIDE图标.双击启动,界面如下:
- Git hub加载慢?下载慢?浏览慢?几个小技巧让你一键起飞!
记得,那是一个风和日丽,艳阳高照的夜晚,只因为当初的一次回眸,于是便决然走向了程序员的道路,从此,CV大法心中记,代码伴我身. 这一天,正当我打开电脑准备开开心心的使用CV大法完成任务的时候,却恼人的 ...
- java 画 哆啦A梦
package Demo;import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;impo ...
- CentOS 7操作系统基础优化介绍
01 前言 操作系统部署完毕后,需要做一些基础的简单优化操作,可以为系统未来的使用过程带来更多便捷. 02 操作系统安全优化配置 系统安装完毕后,默认系统中会存在两个重要的安全服务程序,建议将其首先进 ...
- 【贪心算法】CF3D Least Cost Bracket Sequence
题目大意 洛谷链接 给一个序列,序列里面会有左括号.问号.右括号.对于一个?而言,可以将其替换为一个(,也可以替换成一个),但是都有相应的代价.问:如何替换使得代价最小.前提是替换之后的序列中,括号是 ...
- 手撸了一个HTTP框架:支持Sprng MVC、IOC、AOP,拦截器,配置文件读取...
https://github.com/Snailclimb/jsoncat :仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架 距离上一次给小伙伴们汇报简易 ...