RxJava2 Scheduler调度器源码分析
发表于 | 更新于 | 分类于 RxJavaScheduler是RxJava对线程控制器的抽象,RxJava内置了多种Scheduler的实现。本文将基于RxJava2版本2.1.16对其进行简要分析。
概要
打开Schedulers类,可以一眼看到RxJava2中内置的几种线程控制器。
public final class Schedulers {
@NonNull
static final Scheduler SINGLE;
@NonNull
static final Scheduler COMPUTATION;
@NonNull
static final Scheduler IO;
@NonNull
static final Scheduler TRAMPOLINE;
@NonNull
static final Scheduler NEW_THREAD;
}
Single表示所有任务将会按照先进先执行的顺序才同一个线程中得以执行。
COMPUTATION适用于计算型密集任务,默认情况下RxJava创建了等同于CPU个数的单线程线程池来执行这些任务。很多文章说到RxJava是创建了一个等同于CPU个数的固定大小线程池来执行COMPUTATION任务,这种说法至少在2.1.16版本是错误的(至于之前的版本,我也懒得验证了),后面源码分析过程中也会提到。
IO适用于IO密集型任务,每当提交任务时,RxJava会先在缓冲池中查找是否有合适的线程池来执行任务,如果没有,则创建一个单线程的线程池用于执行此任务。任务执行完毕,这个线程池会放入缓冲池。缓冲池默认每60秒清空一次。
TRAMPOLINE比较简单,直接在当前线程执行任务。
NEW_THREAD也比较简单,每次创建一个新的单线程线程池来执行任务。
Scheduler
在分析具体的线程控制器之前,需要先理解它们的基类Scheduler的工作机制。
首先,我们要关注的是其scheduleDirect方法,这个方法是RxJava把任务交给线程控制器最先调用的方法。方法源码如下:
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run) {
return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
}
这个方法实现看起来很简单,接受一个Runnable参数,表示需要执行的任务。方法里面只是简单的调用了scheduleDirect的另一个重载方法。接着看:
@NonNull
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker();
final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
DisposeTask task = new DisposeTask(decoratedRun, w);
w.schedule(task, delay, unit);
return task;
}
第一行:创建了一个Worker实例。createWorker()是一个抽象方法,每个线程控制器需要实现自己的Worker。
第二行,对需要执行的任务进行装饰,看一下RxJavaPlugins.onSchedule(run)的代码:
@NonNull
public static Runnable onSchedule(@NonNull Runnable run) {
ObjectHelper.requireNonNull(run, "run is null");
Function<? super Runnable, ? extends Runnable> f = onScheduleHandler;
if (f == null) {
return run;
}
return apply(f, run);
}
通过调试发现,onScheduleHandler实际上为null,也就是说默认情况下这个方法的返回值就是参数run。
第三行:将需要执行的任务和Worker使用DisposeTask关联起来。代码如下:
static final class DisposeTask implements Disposable, Runnable, SchedulerRunnableIntrospection {
@NonNull
final Runnable decoratedRun;
@NonNull
final Worker w;
@Nullable
Thread runner;
DisposeTask(@NonNull Runnable decoratedRun, @NonNull Worker w) {
this.decoratedRun = decoratedRun;
this.w = w;
}
@Override
public void run() {
runner = Thread.currentThread();
try {
decoratedRun.run();
} finally {
dispose();
runner = null;
}
}
@Override
public void dispose() {
if (runner == Thread.currentThread() && w instanceof NewThreadWorker) {
((NewThreadWorker)w).shutdown();
} else {
w.dispose();
}
}
@Override
public boolean isDisposed() {
return w.isDisposed();
}
@Override
public Runnable getWrappedRunnable() {
return this.decoratedRun;
}
}
这个DisposeTask将任务和Worker进行了关联,其本身也表示是一种可释放的任务,释放的是什么?释放的就是任务关联的Worker。对NewThreadWorker(对应NEW_THREAD模式)进行了特殊处理,在释放的时候关闭其线程池。
第四行:将任务交给Worker进行处理。
整理上逻辑非常清晰。首先创建一个Worker,然后将任务和Worker进行关联方便之后的资源释放,最后就是将任务交由Worker进行处理。
Schedulers.single
Schedulers.single对应的线程控制器实现是:io.reactivex.internal.schedulers.SingleScheduler。
我们直接看其createWorker()方法的实现:
@NonNull
@Override
public Worker createWorker() {
return new ScheduledWorker(executor.get());
}
实现看起来比较简单,创建了一个ScheduledWorker实例,ScheduledWorker实例接受一个ScheduledExecutorService类型的参数。看到ScheduledExecutorService就代表我们看到线程池了,这个线程池是通过executor.get()得到的。我们看一下这个executor相关的代码(代码经过简化):
public final class SingleScheduler extends Scheduler {
final AtomicReference<ScheduledExecutorService> executor = new AtomicReference<ScheduledExecutorService>();
public SingleScheduler(ThreadFactory threadFactory) {
executor.lazySet(createExecutor(threadFactory));
}
static ScheduledExecutorService createExecutor(ThreadFactory threadFactory) {
return SchedulerPoolFactory.create(threadFactory);
}
}
可以看到,executor是一个AtomicReference 类型的实例。AtomicReference内部使用CAS机制来实现引用类型写操作的原子性,更多内容大家需要了解的话可以自行搜索,本篇文章不过多展开。在这里,姑且把AtomicReference 看作ScheduledExecutorService的实例的容器,调用AtomicReference的get方法获得ScheduledExecutorService的实例,调用AtomicReference的set方法设置一个新的ScheduledExecutorService实例。
在SingleScheduler的构造函数里面对executor进行了赋值操作,最终其值为:
public static ScheduledExecutorService create(ThreadFactory factory) {
final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);
tryPutIntoPool(PURGE_ENABLED, exec);
return exec;
}
可以看到,实际上executor内部就是一个固定大小为1的线程池。也就是说,使用Scheduler.single模式的线程控制器,最终任务都是交由这个固定大小为1的线程池来执行的,所以也叫single模式。
Schedulers.computation
Schedulers.computation对应的线程控制器实现是:io.reactivex.internal.schedulers.ComputationScheduler。
同样,直接看createWorker的实现:
@NonNull
@Override
public Worker createWorker() {
return new EventLoopWorker(pool.get().getEventLoop());
}
方法本身很简单,创建了一个EventLoopWorker实例。参数中的pool是AtomicReference 类型,我们看一下它的初始化过程(经过简化):
public final class ComputationScheduler extends Scheduler implements SchedulerMultiWorkerSupport {
static final int MAX_THREADS;
final AtomicReference<FixedSchedulerPool> pool;
static {
MAX_THREADS = cap(Runtime.getRuntime().availableProcessors(), Integer.getInteger(KEY_MAX_THREADS, 0));
}
public ComputationScheduler(ThreadFactory threadFactory) {
start();
}
@Override
public void start() {
FixedSchedulerPool update = new FixedSchedulerPool(MAX_THREADS, threadFactory);
if (!pool.compareAndSet(NONE, update)) {
update.shutdown();
}
}
}
首先可以看到,MAX_THREADS默认情况下就是CPU的个数。
接下来在start方法中对pool进行了赋值操作,其值为一个FixedSchedulerPool的实例。看来还得看看FixedSchedulerPool是一个什么东西。
//代码经过简化
static final class FixedSchedulerPool implements SchedulerMultiWorkerSupport {
final int cores;
final PoolWorker[] eventLoops;
long n;
FixedSchedulerPool(int maxThreads, ThreadFactory threadFactory) {
this.cores = maxThreads;
this.eventLoops = new PoolWorker[maxThreads];
for (int i = 0; i < maxThreads; i++) {
this.eventLoops[i] = new PoolWorker(threadFactory);
}
}
public PoolWorker getEventLoop() {
return eventLoops[(int)(n++ % cores)];
}
}
可以看到,FixedSchedulerPool在创建的时候构造了一个等同于CPU个数大小的PoolWorker数组。
我们先回顾一下,任务交给Scheduler之后,最开始调用的是Scheduler.scheduleDirect方法,这个方法最终创建了一个新的Worker实例并且把任务交由这个Worker进行处理。也就是说,每个任务都需要创建一个Worker来对其进行处理执行。
接下来回到ComputationScheduler.createWorker方法:
@NonNull
@Override
public Worker createWorker() {
return new EventLoopWorker(pool.get().getEventLoop());
}
其中的pool.get().getEventLoop()参数部分上面已经分析过了,pool.get()实际上是一个FixedSchedulerPool实例。其getEventLoop方法实际上就是从预生成的PoolWorker数组中拿出了一个PoolWorker实例。接下来需要看一下EventLoopWorker的实现:
//代码经过简化
static final class EventLoopWorker extends Scheduler.Worker {
private final PoolWorker poolWorker;
EventLoopWorker(PoolWorker poolWorker) {
this.poolWorker = poolWorker;
}
@NonNull
@Override
public Disposable schedule(@NonNull Runnable action) {
return poolWorker.scheduleActual(action, 0, TimeUnit.MILLISECONDS, serial);
}
}
EventLoopWorker的实现很简单,需要执行的任务最终交由PoolWorker.scheduleActual进行处理。看一下PoolWorker的实现:
static final class PoolWorker extends NewThreadWorker {
PoolWorker(ThreadFactory threadFactory) {
super(threadFactory);
}
}
PoolWorker看起来是一个标志类,所有逻辑都在NewThreadWorker中。
//代码经过简化
public class NewThreadWorker extends Scheduler.Worker implements Disposable {
private final ScheduledExecutorService executor;
public NewThreadWorker(ThreadFactory threadFactory) {
executor = SchedulerPoolFactory.create(threadFactory);
}
@NonNull
public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
Future<?> f;
try {
if (delayTime <= 0) {
f = executor.submit((Callable<Object>)sr);
} else {
f = executor.schedule((Callable<Object>)sr, delayTime, unit);
}
sr.setFuture(f);
} catch (RejectedExecutionException ex) {}
return sr;
}
}
主要逻辑都在scheduleActual方法中,核心逻辑就是把任务交由executor执行。这个executor在构造函数赋值,其值就是一个单线程的线程池。(SchedulerPoolFactory.create()方法在上面分析single模式时介绍过,这里不重复介绍了)
总结一下,任务交由ComputationScheduler之后,创建了一个EventLoopWorker实例来处理任务,这个EventLoopWorker又把任务交给了PoolWorker来处理。值得一提的是在初始化ComputationScheduler的时候创建了等同于CPU个数的PoolWorker,所有的任务都是重用这些PoolWorker来处理的。每个PoolWorker内部包含了一个单线程的线程池,最终任务也是交由这个线程池来执行的。
Schedulers.io
Schedulers.io对应的线程控制器实现是:io.reactivex.internal.schedulers.IoScheduler。
通过上面的single和computation模式的分析,相信大家对Scheduler的机制已经有一定的了解了,下面的分析将会写的简要一些避免造成文章的重复。
直接看createWorker方法:
@NonNull
@Override
public Worker createWorker() {
return new EventLoopWorker(pool.get());
}
接着看EventLoopWorker:
//代码经过简化
static final class EventLoopWorker extends Scheduler.Worker {
private final CompositeDisposable tasks;
private final CachedWorkerPool pool;
private final ThreadWorker threadWorker;
final AtomicBoolean once = new AtomicBoolean();
EventLoopWorker(CachedWorkerPool pool) {
this.pool = pool;
this.tasks = new CompositeDisposable();
this.threadWorker = pool.get();
}
@NonNull
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}
}
可以看到,任务最终交由threadWorker.scheduleActual进行处理,threadWorker在构造函数中被赋值,其值通过CachedWorkerPool.get得到。先看看CachedWorkerPool的代码:
static final class CachedWorkerPool implements Runnable {
private final long keepAliveTime;
private final ConcurrentLinkedQueue<ThreadWorker> expiringWorkerQueue;
final CompositeDisposable allWorkers;
private final ScheduledExecutorService evictorService;
private final Future<?> evictorTask;
private final ThreadFactory threadFactory;
CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
this.keepAliveTime = unit != null ? unit.toNanos(keepAliveTime) : 0L;
this.expiringWorkerQueue = new ConcurrentLinkedQueue<ThreadWorker>();
this.allWorkers = new CompositeDisposable();
this.threadFactory = threadFactory;
ScheduledExecutorService evictor = null;
Future<?> task = null;
if (unit != null) {
evictor = Executors.newScheduledThreadPool(1, EVICTOR_THREAD_FACTORY);
task = evictor.scheduleWithFixedDelay(this, this.keepAliveTime, this.keepAliveTime, TimeUnit.NANOSECONDS);
}
evictorService = evictor;
evictorTask = task;
}
@Override
public void run() {
evictExpiredWorkers();
}
ThreadWorker get() {
if (allWorkers.isDisposed()) {
return SHUTDOWN_THREAD_WORKER;
}
while (!expiringWorkerQueue.isEmpty()) {
ThreadWorker threadWorker = expiringWorkerQueue.poll();
if (threadWorker != null) {
return threadWorker;
}
}
// No cached worker found, so create a new one.
ThreadWorker w = new ThreadWorker(threadFactory);
allWorkers.add(w);
return w;
}
void release(ThreadWorker threadWorker) {
// Refresh expire time before putting worker back in pool
threadWorker.setExpirationTime(now() + keepAliveTime);
expiringWorkerQueue.offer(threadWorker);
}
void evictExpiredWorkers() {
if (!expiringWorkerQueue.isEmpty()) {
long currentTimestamp = now();
for (ThreadWorker threadWorker : expiringWorkerQueue) {
if (threadWorker.getExpirationTime() <= currentTimestamp) {
if (expiringWorkerQueue.remove(threadWorker)) {
allWorkers.remove(threadWorker);
}
} else {
// Queue is ordered with the worker that will expire first in the beginning, so when we
// find a non-expired worker we can stop evicting.
break;
}
}
}
}
}
代码有点多,但不要被吓着,其逻辑本身细看的话是很容易理解的。
主要有以下几点:
- 构造函数中开启了一个单线程的线程池,这个线程池的功能就是周期性的(默认是60秒)清理不再需要的ThreadWorker。
- 调用
CachedWorkerPool.get获取ThreadWorker时,会先从缓冲池中获取,缓冲池中没有合适的ThreadWorker,才会创建新的。
也就是说CachedWorkerPool的职责就是维护一组ThreadWorker,当新任务来临时负责找到一个ThreadWorker来执行任务。
接下来看看ThreadWorker的代码逻辑:
static final class ThreadWorker extends NewThreadWorker {
private long expirationTime;
ThreadWorker(ThreadFactory threadFactory) {
super(threadFactory);
this.expirationTime = 0L;
}
public long getExpirationTime() {
return expirationTime;
}
public void setExpirationTime(long expirationTime) {
this.expirationTime = expirationTime;
}
}
基本没什么,主要逻辑还是在NewThreadWorker里面。上面分析computation模式时已经分析过NewThreadWorker了,其内部维护了一个单线程的线程池,收到的新任务都会交给这个单线程线程池来进行处理。
总结一下,io模型下,收到新任务时,可能会创建一个新的NewThreadWorker来执行这个任务,也可能会复用之前的NewThreadWorker,具体取决于新任务的耗时程度。可以想到,默认情况下,如果60秒内执行相当多的io任务,且每个io任务的耗时都较长的话,RxJava内部会创建非常之多的线程池,这样会降低系统性能,所以io型模式不适用于长时间执行的任务。
Schedulers.trampoline
Schedulers.trampoline对应的线程控制器实现是:io.reactivex.internal.schedulers.TrampolineScheduler。
它的实现很简单。内部重写了scheduleDirect方法,所以就不应该从createWorker看起了。
@NonNull
@Override
public Disposable scheduleDirect(@NonNull Runnable run) {
RxJavaPlugins.onSchedule(run).run();
return EmptyDisposable.INSTANCE;
}
简单粗暴,就是把run拿来执行了一遍。
Schedulers.newThread
Schedulers.newThread对应的线程控制器实现是:io.reactivex.internal.schedulers.NewThreadScheduler。
@NonNull
@Override
public Worker createWorker() {
return new NewThreadWorker(threadFactory);
}
NewThreadWorker见过很多次了,内部会把任务交给一个单线程的线程池来执行。
NewThreadScheduler里面并没有实现线程池的清理工作。那线程池时什么时候清理的?还记得最开始分析Scheduler时,里面的DisposeTask吗?newThread模式下,当DisposeTask执行完毕时,会自动关闭线程池。