一、subcribeOn
1.1 测试代码
CountDownLatch countDownLatch = new CountDownLatch(1);
//模拟Io阻塞
private String ioBlockOperator(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getThreadName()+" success";
}
//单元测试方法
@Test
public void test1() throws Exception {
Mono<String> mono = Mono.fromCallable(()->{
return ioBlockOperator();
});
mono.subscribeOn(Schedulers.newElastic("zhw-elastic"))
.subscribe(System.out::println,e->{},countDownLatch::countDown);
countDownLatch.await();
}
1.2 分析第一步fromCallable
public static <T> Mono<T> fromCallable(Callable<? extends T> supplier) {
return onAssembly(new MonoCallable<>(supplier));
}
一个callable
被封装到MonoCallable
中
类MonoCallable
UML图
可以看到,这是个Callable的子类。
因为在本例中订阅者不是直接订阅该类,所以该类的其他细节就不需要分析了。
1.3 开始重点分析第subscribeOn
首先分析subscribeOn
方法的参数类型Scheduler
,这是个调度器,他的底层应该用ExecutorService
或者ScheduledExecutorService
去初始化实现。可以理解这个是个线程池的调度器
subscribeOn
方法的定义
public final Mono<T> subscribeOn(Scheduler scheduler) {
if(this instanceof Callable) {
if (this instanceof Fuseable.ScalarCallable) {
try {
T value = block();
return onAssembly(new MonoSubscribeOnValue<>(value, scheduler));
}
catch (Throwable t) {
//leave MonoSubscribeOnCallable defer error
}
}
@SuppressWarnings("unchecked")
Callable<T> c = (Callable<T>)this;
return onAssembly(new MonoSubscribeOnCallable<>(c,
scheduler));
}
return onAssembly(new MonoSubscribeOn<>(this, scheduler));
}
经过subscribeOn
后上面的MonoCallable
会被转成一个Callable
然后作为参数去初始化MonoSubscribeOnCallable
这个类。下面开始分析MonoSubscribeOnCallable
UML
所以MonoSubscribeOnCallable
其实就是一个Mono的子类。就是具体的Mono类。根据前面的经验需要还需要找到中间类subscripton
,然后分析subscripton
的request方法即可。下面看当开始订阅的方法
public void subscribe(CoreSubscriber<? super T> actual) {
//中间类
FluxSubscribeOnCallable.CallableSubscribeOnSubscription<T> parent =
new FluxSubscribeOnCallable.CallableSubscribeOnSubscription<>(actual, callable, scheduler);
//订阅者开始订阅中间类
actual.onSubscribe(parent);
try {
parent.setMainFuture(scheduler.schedule(parent));
}
catch (RejectedExecutionException ree) {
if(parent.state != FluxSubscribeOnCallable.CallableSubscribeOnSubscription.HAS_CANCELLED) {
actual.onError(Operators.onRejectedExecution(ree, actual.currentContext()));
}
}
}
- 中间类
CallableSubscribeOnSubscription
该类的UML图
信息量有点大 - 是个Subscription
- 是个队列
- 还是一个Runable
他的构造方法
CallableSubscribeOnSubscription(CoreSubscriber<? super T> actual,
Callable<? extends T> callable,
Scheduler scheduler) {
this.actual = actual;//订阅者
this.callable = callable;//发布者
this.scheduler = scheduler;//调度器
}
当订阅者开始订阅中间类的时候
actual.onSubscribe(parent);
public final void onSubscribe(Subscription s) {
if (Operators.validate(subscription, s)) {
this.subscription = s;
if (subscriptionConsumer != null) {
try {
subscriptionConsumer.accept(s);
}
catch (Throwable t) {
Exceptions.throwIfFatal(t);
s.cancel();
onError(t);
}
}
else {
//老套路,开始中间类开始调用request方法
s.request(Long.MAX_VALUE);
}
}
}
开始分析中间类的request
方法
/**
* static final int NO_REQUEST_HAS_VALUE = 1;
* static final int HAS_REQUEST_NO_VALUE = 2;
* static final int HAS_REQUEST_HAS_VALUE = 3;
* static final int HAS_CANCELLED = 4;
*/
public void request(long n) {
if (Operators.validate(n)) {
for (; ; ) {
int s = state;//初始化是0
if (s == HAS_CANCELLED || s == HAS_REQUEST_NO_VALUE || s == HAS_REQUEST_HAS_VALUE) {
return;
}
if (s == NO_REQUEST_HAS_VALUE) {
if (STATE.compareAndSet(this, s, HAS_REQUEST_HAS_VALUE)) {
try {
Disposable f = scheduler.schedule(this::emitValue);
setRequestFuture(f);
}
catch (RejectedExecutionException ree) {
actual.onError(Operators.onRejectedExecution(ree,
actual.currentContext()));
}
}
return;
}
if (STATE.compareAndSet(this, s, HAS_REQUEST_NO_VALUE)) {
return;
}
}
}
}
本例中其实request方法没走什么只是把STATE设置成HAS_REQUEST_NO_VALUE然后就return了
紧接着开始分析
parent.setMainFuture(scheduler.schedule(parent));
scheduler.schedule(parent)
这句话很明显就是调度器开始工作了
public Disposable schedule(Runnable task) {
//封装一个ExecutorService
CachedService cached = pick();
//开始真正的调度
return Schedulers.directSchedule(cached.exec,
new DirectScheduleTask(task, cached),
0L,
TimeUnit.MILLISECONDS);
}
注意重点Scheduler
有四个实现
static final String ELASTIC = "elastic"; // IO stuff
static final String PARALLEL = "parallel"; //scale up common tasks
static final String SINGLE = "single"; //non blocking tasks
static final String IMMEDIATE = "immediate";
static final String FROM_EXECUTOR = "fromExecutor";
static final String FROM_EXECUTOR_SERVICE = "fromExecutorService";
因为我们这次使用的是ELASTIC,所以本次创建的ExecutorService
是ScheduledExecutorService
,他的线程池定义如下
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
其中
- corePoolSize是1
- maximumPoolSize是无穷大
紧接着开始真正的调度
Schedulers.directSchedule();
static Disposable directSchedule(ScheduledExecutorService exec,
Runnable task,
long delay,
TimeUnit unit) {
SchedulerTask sr = new SchedulerTask(task);
Future<?> f;
if (delay <= 0L) {
f = exec.submit((Callable<?>) sr);/在新线程中执行任务,得到Future
}
else {
f = exec.schedule((Callable<?>) sr, delay, unit);
}
sr.setFuture(f);
return sr;
}
这个方法很简单明了,就是用指定的ExecutorService去submit或者schedule执行然后得到一个结果,结果是Future,最后把Future封装到SchedulerTask
中。
当执行f = exec.submit((Callable<?>) sr)
的时候,注意力就在sr这Runable
上面了,从上面可知sr是new DirectScheduleTask(task, cached)
task就是那个Subscription也就是FluxSubscribeOnCallable.CallableSubscribeOnSubscription
,上面说信息量有点大的那个
- 是个Subscription
- 是个队列
- 还是一个Runable
现在既然是Runable
方法,那么我们就可以吧注意力集中到run
方法了
public void run() {
T v;
try {
v = callable.call();//阻塞的等待获取结果
}
catch (Throwable ex) {
actual.onError(Operators.onOperatorError(this, ex,
actual.currentContext()));
return;
}
if (v == null) {
fusionState = COMPLETE;
actual.onComplete();
return;
}
for (; ; ) {
int s = state;
if (s == HAS_CANCELLED || s == HAS_REQUEST_HAS_VALUE || s == NO_REQUEST_HAS_VALUE) {
return;
}
if (s == HAS_REQUEST_NO_VALUE) {
if (fusionState == NO_VALUE) {
this.value = v;
this.fusionState = HAS_VALUE;
}
//执行订阅者的onNext方法
actual.onNext(v);
if (state != HAS_CANCELLED) {
actual.onComplete();
}
return;
}
this.value = v;
if (STATE.compareAndSet(this, s, NO_REQUEST_HAS_VALUE)) {
return;
}
}
}
这几个状态还没搞清楚,待补充
actual.onNext(v);
得到结果后传给订阅者,好了整个流程就结束了。
二、publishOn
2.1 方法定义
public final Mono<T> publishOn(Scheduler scheduler) {
if(this instanceof Callable) {
if (this instanceof Fuseable.ScalarCallable) {
try {
T value = block();
return onAssembly(new MonoSubscribeOnValue<>(value, scheduler));
}
catch (Throwable t) {
//leave MonoSubscribeOnCallable defer error
}
}
@SuppressWarnings("unchecked")
Callable<T> c = (Callable<T>)this;
return onAssembly(new MonoSubscribeOnCallable<>(c, scheduler));
}
return onAssembly(new MonoPublishOn<>(this, scheduler));
}
方法的定义基本和subscribeOn一模一样,唯一的区别是在最后一句话publishOn是返回MonoPublishOn
,而subscribeOn返回MonoSubscribeOn
2.2 分析MonoPublishOn
首先UML图
他就是一个实际发布者,所以根据以前的经验需要看subscribe
方法
public void subscribe(CoreSubscriber<? super T> actual) {
source.subscribe(new PublishOnSubscriber<T>(actual, scheduler));
}
- source:该发布者的上级
- PublishOnSubscriber:充当双重身份,从名字上看他是个订阅者,同时它也是个subscription(中间人,维护订阅者和发布者)
如果对Map,flatMap稍微了解,可知这个subscribe方法基本是map等是基本一样的,他没有逻辑,就是构建逐级订阅模型,如果有时间我会分析逐级订阅
分析PublishOnSubscriber
UML
从UML图中发现他还是实现了Runable接口,其实也可以理解,因为他持有scheduler主要目的就是异步运行自己的任务。
既然是个订阅者,分析它的onNext
方法
public void onNext(T t) {
value = t;
trySchedule(this, null, t);
}
void trySchedule(
@Nullable Subscription subscription,
@Nullable Throwable suppressed,
@Nullable Object dataSignal) {
if(future != null){
return;
}
try {
future = this.scheduler.schedule(this);
}
catch (RejectedExecutionException ree) {
actual.onError(Operators.onRejectedExecution(ree, subscription,
suppressed, dataSignal, actual.currentContext()));
}
}
可知他接受上游的数据,然后可以运行调度器,把自己当成任务,既然是任务分析它的run
方法
public void run() {
if (OperatorDisposables.isDisposed(future)) {
return;
}
T v = (T)VALUE.getAndSet(this, null);
if (v != null) {
actual.onNext(v);
actual.onComplete();
}
else {
Throwable e = error;
if (e != null) {
actual.onError(e);
}
else {
actual.onComplete();
}
}
}
链式调用下级的onNext方法。
可知publishOn方法影响的下游的执行线程环境
2.3 代码示例
public void test5() throws Exception{
Mono.fromCallable(()->ioBlockOperator())
.map(str->{
System.out.println("map1 thread name:"+Thread.currentThread().getName());
return str+"_map";
})
.publishOn(Schedulers.newParallel("parallel_position1"))
.subscribe(str->{
System.out.println("subscript thread name:"+Thread.currentThread().getName());
},e->{},countDownLatch::countDown);
countDownLatch.await();
}
输出
IO Thread name: main
map1 thread name:main
//publishOn之前都是main线程,之后都是parallel线程
subscript thread name:parallel_position1-1
三、两者的区别
两者的区别其实就是分析MonoPublishOn
和MonoSubscribeOn
,上面已经分析MonoPublishOn
了,下面简单分析MonoSubscribeOn
老规矩UML图
分析可知也是一个实际的发布者,下面也开始分析它的subscribe
方法
public void subscribe(CoreSubscriber<? super T> actual) {
Scheduler.Worker worker = scheduler.createWorker();
SubscribeOnSubscriber<T> parent = new SubscribeOnSubscriber<>(source,
actual, worker);
actual.onSubscribe(parent);
try {
worker.schedule(parent);
}
catch (RejectedExecutionException ree) {
if (parent.s != Operators.cancelledSubscription()) {
actual.onError(Operators.onRejectedExecution(ree, parent, null, null,
actual.currentContext()));
}
}
}
这个就和MonoPublishOn
差别巨大了,可见他不是为了构建逐级订阅了
- 首先构建一个subscription
- 然后让下游订阅器订阅该subscription
- 在调度器中执行上游的数据处理
简单来说两者区别是一个影响上游,一个影响下游的执行环境,但是还有一些细微区别,我有时间在分析