一起看看RxJava的Scheduler吧!

1,547 阅读10分钟

前言

前面用了两篇文章介绍了一下Rx的封装套路以及涉及线程调度流程控制的subscribeOn与observeOn源码。本文会对Android开发中常用的IoScheduler和AndroidSchedulers.mainThread()进行源码解析。前文也提到过,subscribeOn与observeOn只是基于开发者设置的scheduler在流程上进行线程调度的控制。这里可以理解为相对于subscribeOn与observeOn,scheduler只是它们的工具。 所以本文将重点看看scheduler的实现(ps:本文基于RxJava 2.1.16,RxAndroid 2.0.1)。

Rx的Observable.create干了啥?

RxJava的subscribeOn与observeOn源码解析

IoScheduler

基于subscribeOn在subscribeActual时的调用,整理了一个流程图: 上面给出的是从subscribeActual调用scheduler开始到提交给线程池的数据流。接下来我们将按这个过程来解析IoScheduler。

Observable.create<String> {
     Log.d(TAG, "事件产生线程:${Thread.currentThread().name}")
     it.onNext("I'm ")
     it.onComplete()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
      Log.d(TAG, "事件消费线程:${Thread.currentThread().name}")
      Log.d(TAG, it)
}

Scheduler

Scheduler.java是RxJava中线程调度者scheduler的父类。ObservableSubscribeOn.subscribeActual方法中,最终会调用scheduler.scheduleDirect进行线程切换。这里的scheduler为IoScheduler,scheduleDirect方法在其父类实现

  • 调用子类实现的createWorker方法。
  • 利用Scheduler.DisposeTask类(也是一个Runnable)包装传入的Runnable对象
  • 调用Scheduler.Worker对象的schedule方法进行线程调度。 接下来,以此这个方法中为基础,进行逐一解析。
// ObservableSubscribeOn.java
@Override
public void subscribeActual(final Observer<? super T> s) {
    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
    s.onSubscribe(parent);
    
    parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}

// Scheduler.java
@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;
}
...
@NonNull
public abstract Worker createWorker();

线程调度

在IoScheduler中,会涉及到两个逻辑的线程池:1、工作线程池,用于处理外部提交过来的任务(譬如开发者设置的订阅或观察的响应。)2、回收线程池,定期回收上述过期的工作线程池的线程池。

这里先说一下IoScheduler的内部类,好让大家对它们有初步认识。

  • IoScheduler.ThreadWorker:IoScheduler中真正管理线程的类,成员变量有一个Java线程池,用于线程调度。
  • IoScheduler.CachedWorkerPool:这是一个工厂角色的类型,主要用于创建、缓存复用和销毁IoScheduler.ThreadWorker
  • IoScheduler.EventLoopWorker:IoScheduler.ThreadWorker的装饰器,用于控制CachedWorkerPool管理它自身持有的ThreadWorker

成员变量

先来看看IoScheduler的成员变量及其定义:

  • 这里的XXX_THREAD_NAME_PREFIX及RxThreadFactory对象为用于线程池创建线程的配置,RxThreadFactory为Java的ThreadFactory子类,这里主要用于规范线程的名称(name)及优先级(priority)。在IoScheduler的静态代码块用到。
  • KEEP_ALIVE_TIME、KEEP_ALIVE_UNIT:工作线程调用结束后默认的存活时间。后续的start()方法会用到。
  • SHUTDOWN_THREAD_WORKER:IoScheduler.ThreadWorker对象,用于在Rx的dipose调用后的线程后续处理。
  • threadFactory:这是一个在构造方法可自定义设置的ThreadFactory,用于覆盖上述的WORKER_THREAD_FACTORY。
  • pool:IoScheduler.CachedWorkerPool对象,这是一个类似工厂模式的对象,主要用于创建上述的IoScheduler.ThreadWorker对象,管理及复用线程

ps:这里可以简单阅读有一个了解,后面会详细介绍并将它们在流程上串联起来。

// IoScheduler.java
public final class IoScheduler extends Scheduler {
    private static final String WORKER_THREAD_NAME_PREFIX = "RxCachedThreadScheduler";
    static final RxThreadFactory WORKER_THREAD_FACTORY;

    private static final String EVICTOR_THREAD_NAME_PREFIX = "RxCachedWorkerPoolEvictor";
    static final RxThreadFactory EVICTOR_THREAD_FACTORY;

    private static final long KEEP_ALIVE_TIME = 60;
    private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.SECONDS;

    static final ThreadWorker SHUTDOWN_THREAD_WORKER;
    final ThreadFactory threadFactory;
    final AtomicReference<CachedWorkerPool> pool;

    /** The name of the system property for setting the thread priority for this Scheduler. */
    private static final String KEY_IO_PRIORITY = "rx2.io-priority";

    static final CachedWorkerPool NONE;

静态代码块

这里主要是创建上述提到的一些静态变量,这里的NONE可以理解为当前IoScheduler的默认CachedWorkerPool对象。用于后续start()和shutdown()方法的更新替换。

KEY_IO_PRIORITY = "rx2.io-priority"是设置线程的优先级,可以通过系统属性设置key为"rx2.io-priority"修改

static {
    SHUTDOWN_THREAD_WORKER = new ThreadWorker(new RxThreadFactory("RxCachedThreadSchedulerShutdown"));
    SHUTDOWN_THREAD_WORKER.dispose();

    int priority = Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, Integer.getInteger(KEY_IO_PRIORITY, Thread.NORM_PRIORITY)));

    WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);

    EVICTOR_THREAD_FACTORY = new RxThreadFactory(EVICTOR_THREAD_NAME_PREFIX, priority);

    NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
    NONE.shutdown();
}

初始化、启动及关闭

Schedulers.io()的调用对应的是默认全局静态IoScheduler对象,用无参构造方法创建。

  • threadFactory对象默认为WORKER_THREAD_FACTORY对象。
  • 先利用默认CachedWorkerPool对象NONE初始化pool,再调用start()方法。
  • start()方法,真正创建一个CachedWorkerPool对象,设置默认线程池存活时间KEEP_ALIVE_TIME(60s),进行替换。ps:这里运用到CAS保证线程安全。
  • 后续的shutdown()方法则是start()的重置操作。ps:这里也运用到了自旋机制保证线程安全。

其实笔者个人不是特别理解这里为什么需要进行start、shutdown的逻辑,如果有懂的朋友可以在下方评论交流。

// IoScheduler.java
public IoScheduler() {
    this(WORKER_THREAD_FACTORY);
}

public IoScheduler(ThreadFactory threadFactory) {
    this.threadFactory = threadFactory;
    this.pool = new AtomicReference<CachedWorkerPool>(NONE);
    start();
}

@Override
public void start() {
    CachedWorkerPool update = new CachedWorkerPool(KEEP_ALIVE_TIME, KEEP_ALIVE_UNIT, threadFactory);
    if (!pool.compareAndSet(NONE, update)) {
        update.shutdown();
    }
}

@Override
public void shutdown() {
    for (;;) {
       CachedWorkerPool curr = pool.get();
       if (curr == NONE) {
          return;
       }
       if (pool.compareAndSet(curr, NONE)) {
          curr.shutdown();
          return;
       }
    }
}

createWorker

回归正题,Scheduler.scheduleDirect方法会调用createWorker()。在IoScheduler中createWorker()会创建并返回EventLoopWorker对象,而EventLoopWorker的成员变量CachedWorkerPool pool即为IoScheduler的pool对象

// IoScheduler.java
@NonNull
@Override
public Worker createWorker() {
    return new EventLoopWorker(pool.get());
}

EventLoopWorker

EventLoopWorker主要包含了从IoScheduler获取过来的CachedWorkerPool和一个ThreadWorker。

前面提到,CachedWorkerPool在EventLoopWorker中充当对于ThreadWorker的管理。ThreadWorker则利用内部包含的线程池进行线程调度

在EventLoopWorker的构造方法中,threadWorker通过CachedWorkerPool.get()获取

ps:CachedWorkerPool.get方法中包含对于ThreadWorker的复用逻辑,这个后面会讲到。

// IoScheduler.java - class 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();
    }
    ...

Scheduler.scheduleDirect方法的最后会调用w.schedule,这里实际会调用threadWorker.scheduleActual

// IoScheduler.java - class EventLoopWorker
@NonNull
@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
    if (tasks.isDisposed()) {
        // don't schedule, we are unsubscribed
        return EmptyDisposable.INSTANCE;
    }

    return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}

ThreadWorker

ThreadWorker的scheduleActual继承自其父类NewThreadWorker:

  • scheduleActual方法中真正调用Java线程池executor进行线程调度
  • 这里利用ScheduledRunnable类再次对传入的Runnable对象进行装饰。
  • 以ObservableSubscribeOn触发为例,Runnable对象的装饰链为SubscribeTask -> Scheduler.DisposeTask(Scheduler.scheduleDirect方法)-> ScheduledRunnable(NewThreadWorker.scheduleActual)
// IoScheduler.java - class ThreadWorker
static final class ThreadWorker extends NewThreadWorker {
        private long expirationTime;
        ...

// NewThreadWorker.java
@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);

    if (parent != null) {
        if (!parent.add(sr)) {
            return sr;
        }
    }

    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) {
        if (parent != null) {
            parent.remove(sr);
        }
        RxJavaPlugins.onError(ex);
    }

    return sr;
}

NewThreadWorker内的默认会创建一个单线程的线程池。这里就要理解一个概念,IoScheduler内不是只有一个线程池进行调度,而是有多个单线程的线程池进行调度。对于每个线程池的管理者为IoScheduler.ThreadWorker

// NewThreadWorker.java
public class NewThreadWorker extends Scheduler.Worker implements Disposable {
    private final ScheduledExecutorService executor;

    volatile boolean disposed;

    public NewThreadWorker(ThreadFactory threadFactory) {
        executor = SchedulerPoolFactory.create(threadFactory);
    }
    ...

// SchedulerPoolFactory.java
public static ScheduledExecutorService create(ThreadFactory factory) {
    final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);
    tryPutIntoPool(PURGE_ENABLED, exec);
    return exec;
}    

CachedWorkerPool

填补一下小尾巴,说说CachedWorkerPool.get方法:

  • get()方法最终返回一个ThreadWorker对象,可以理解为一个工厂角色。
  • expiringWorkerQueue为CachedWorkerPool中对于ThreadWorker的缓存队列(ConcurrentLinkedQueue<ThreadWorker>类型)。
  • 若expiringWorkerQueue不为空则从中取出一个ThreadWorker返回,否则创建一个新的ThreadWorker返回

ps:需要注意的是,ThreadWorker加入expiringWorkerQueue的时机并不在此,而在Runnable执行结束后。这个后面会讲到。

// IoScheduler.java - class CachedWorkerPool
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;
}

ThreadWorker管理与销毁

在执行完Rx传递过来的Runnable后会存在回收资源问题,接下来说说关于ThreadWorker的管理策略。

在之前的Scheduler.scheduleDirect方法中,还遗漏了Scheduler.DisposeTask(继承自Runnable)。这里初始化时传入的decoratedRun即为上一步传入的SubscribeTask对象,w为上述提到的EventLoopWorker

// Scheduler.java
@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);
    ...

在run方法被调用时,

  • 首先调用的是成员变量Runnable对象的run方法(ps:这里是SubscribeTask,执行下一步订阅逻辑)。
  • 最后调用自身的dispose()方法,由于w为EventLoopWorker,故会执行EventLoopWorker.dispose()方法
// Scheduler.java - class DisposeTask
@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();
    }
}

EventLoopWorker.dispose方法会调用pool.release,这里的pool就是CachedWorkerPool对象,由此也能看出EventLoopWorker有操控CachedWorkerPool对于ThreadWorker管理的作用

// IoScheduler.java - class EventLoopWorker
@Override
public void dispose() {
    if (once.compareAndSet(false, true)) {
        tasks.dispose();

        // releasing the pool should be the last action
        pool.release(threadWorker);
    }
}

这里会修改前面我们提到ThreadWorker的expirationTime属性,该过期时间会设置为当前时间+keepAliveTime(默认60s)。然后将threadWorker加入到expiringWorkerQueue队列中。

// IoScheduler.java - class CachedWorkerPool
void release(ThreadWorker threadWorker) {
    // Refresh expire time before putting worker back in pool
    threadWorker.setExpirationTime(now() + keepAliveTime);

    expiringWorkerQueue.offer(threadWorker);
}

CachedWorkerPool其实也是一个Runnable对象,前面提到CachedWorkerPool的另一个作用是定期销毁已过期的IoScheduler.ThreadWorker对象evictor为执行销毁的线程池,以CachedWorkerPool作为Runnable对象。(ps:这里设置每keepAliveTime时刻触发一次,即默认为60s一次触发)。

// IoScheduler.java - class CachedWorkerPool
static final class CachedWorkerPool implements Runnable
...
CachedWorkerPool(long keepAliveTime, TimeUnit unit, 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;
}

每触发一次线程调用,就会调用一次evictExpiredWorkers()方法。通过遍历expiringWorkerQueue找到已到过期时间的ThreadWorker并将其remove。达到释放无用的ThreadWorker效果

// IoScheduler.java - class CachedWorkerPool
@Override
public void run() {
    evictExpiredWorkers();
}
...
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;
            }
        }
    }
}

小结

总的来说:

  • ThreadWorker内部持有Java线程池。
  • EventLoopWorker负责触发CachedWorkerPool对于ThreadWorker的创建、复用及销毁
  • CachedWorkerPool内部持有ThreadWorker缓存队列间接管理Java线程池的创建、复用以及销毁
  • 当线程任务执行完后,装饰器DisposeTask会通过触发EventLoopWorker.dispose方法将当前的ThreadWorker加入到CachedWorkerPool的缓存队列,并设置过期时间
  • CachedWorkerPool会通过线程定时触发的机制移除掉已过期的ThreadWorker。
  • 若ThreadWorker为达到过期时间,则会在CachedWorkerPool.get()方法中被复用

ps:有上述的代码分析可知,IoScheduler会在线程资源无法复用的情况下,创建新的线程池以供Rx的线程调度。如果有过多耗时逻辑时可能需要谨慎使用Scheduler.io()

源码:

AndroidSchedulers.mainThread()

AndroidSchedulers顾名思义就是封装有关Android的scheduler的类。关于Android的线程调度就离不开Handler-Looper-Message那一套体系了,关于这套消息传递机制这里不过多介绍。

以下代码可以看到AndroidSchedulers.mainThread()中返回的Scheduler是一个拥有主线程Looper的HandlerScheduler对象。可以推测后续的线程切换操作,是通过Handler的消息传递实现的。 ps:需要注意的是AndroidSchedulers并不是一个Scheduler类型,它的角色类似Schedulers.io()用于创建和提供Scheduler。

// AndroidSchedulers.java
public final class AndroidSchedulers {
    private static final class MainHolder {
        static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()));
    }

    private static final Scheduler MAIN_THREAD = RxAndroidPlugins.initMainThreadScheduler(
            new Callable<Scheduler>() {
                @Override public Scheduler call() throws Exception {
                    return MainHolder.DEFAULT;
                }
            });

    public static Scheduler mainThread() {
        return RxAndroidPlugins.onMainThreadScheduler(MAIN_THREAD);
    }
...

// HandlerScheduler.java
final class HandlerScheduler extends Scheduler {
    private final Handler handler;

    HandlerScheduler(Handler handler) {
        this.handler = handler;
    }

让我们以observeOn为例看看HandlerScheduler的线程调度实现。

Observable.create<String> {
     Log.d(TAG, "事件产生线程:${Thread.currentThread().name}")
     it.onNext("I'm ")
     it.onComplete()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
      Log.d(TAG, "事件消费线程:${Thread.currentThread().name}")
      Log.d(TAG, it)
}

在订阅期间ObservableObserveOn.subscribeActual会调用scheduler.createWorker(),这个和上述的IoScheduler类似。(这里的scheduler根据上述示例代码可理解为AndroidSchedulers.mainThread()返回的HandlerScheduler)。创建后的Scheduler.Worker对象作为ObserveOnObserver,用于后续观察事件更新的线程调度。

HandlerScheduler.createWorker()会创建一个HandlerWorker对象,这里的handler为拥有主线程Looper的Handler(Handler(Looper.getMainLooper()))。

// ObservableObserveOn.java
@Override
protected void subscribeActual(Observer<? super T> observer) {
    if (scheduler instanceof TrampolineScheduler) {
        source.subscribe(observer);
    } else {
        Scheduler.Worker w = scheduler.createWorker();

        source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
    }
}

// HandlerScheduler.java
@Override
public Worker createWorker() {
    return new HandlerWorker(handler);
}

在前文中有提及到,到Rx的事件下发譬如经过observeOn操作符的onNext方法时,会调用ObserveOnObserver.schedule()。worker.schedule(this);及为调用HandlerWorker.schedule。

// ObservableObserveOn.java - class ObserveOnObserver
void schedule() {
    if (getAndIncrement() == 0) {
        worker.schedule(this);
    }
}

HandlerWorker.schedule方法内,将需要执行的Runnable对象利用ScheduledRunnable类装饰,再通过Message的形式利用Handler切换到其所在线程(主线程)执行

private static final class HandlerWorker extends Worker {
    private final Handler handler;
    ...
    @Override
    public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
        if (run == null) throw new NullPointerException("run == null");
        if (unit == null) throw new NullPointerException("unit == null");

        if (disposed) {
            return Disposables.disposed();
        }

        run = RxJavaPlugins.onSchedule(run);

        ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

        Message message = Message.obtain(handler, scheduled);
        message.obj = this; // Used as token for batch disposal of this worker's runnables.

        handler.sendMessageDelayed(message, Math.max(0L, unit.toMillis(delay)));

        if (disposed) {
            handler.removeCallbacks(scheduled);
            return Disposables.disposed();
        }

        return scheduled;
    }
    ...

源码:

总结

最后再总结一下关于Scheduler的通用逻辑:

  • Scheduler.Worker的角色为线程管理,RxJava真正封装线程调度的地方。
  • Scheduler的角色为调度者。负责管理Scheduler.Worker,包括其创建、复用及销毁

最后

本文重点解析Schedulers.io()(IoScheduler)与AndroidSchedulers.mainThread()的源码。这里分享一篇对于其他Scheduler介绍比较全的文章:RxJava之Schedulers源码介绍

系列文章: