二、subscribeOn和publishOn源码解析

一、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
MonoCallableUML图
在这里插入图片描述
可以看到,这是个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这个类。下面开始分析MonoSubscribeOnCallableUML
在这里插入图片描述
所以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,所以本次创建的ExecutorServiceScheduledExecutorService,他的线程池定义如下

  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等是基本一样的,他没有逻辑,就是构建逐级订阅模型,如果有时间我会分析逐级订阅

分析PublishOnSubscriberUML
在这里插入图片描述
从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

三、两者的区别

两者的区别其实就是分析MonoPublishOnMonoSubscribeOn,上面已经分析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
  • 在调度器中执行上游的数据处理

简单来说两者区别是一个影响上游,一个影响下游的执行环境,但是还有一些细微区别,我有时间在分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值