RxJava介绍6:Scheduler调度器

1,455 阅读6分钟

Scheduler

从前述RxJava介绍3:源码解析来看,我们大概知道Scheduler就是把代码场景扔进另外的线程运行。Scheduler正常通过线程池调度任务,特别的是AndroidSchedulers通过切换到主线程Handler运行。

核心代码

下面是一段伪代码

public abstract class Scheduler {
    
    public abstract Worker createWorker();

    public Disposable scheduleDirect(@NonNull Runnable run) {
        createWorker().schedule(run);
    }
    public abstract static class Worker implements Disposable {
        ExecutorService  executor;
        public Disposable schedule(@NonNull Runnable run) {
            Task task = new Task(run);
            executor.submit(task)
        }
    }
    //在RxJava中,Task也许叫ScheduledRunnable,也许叫ScheduledDirectTask
    public static class Task implements Callable<Void>,Disposable{
        private Runnable run;
    	@Override
    	public Void call() {run.run();return null;}
    }
}

当我们添加形如.subscribeOn(Schedulers.io())

public final class SingleSubscribeOn<T> extends Single<T> {
    final Scheduler scheduler;
    
    @Override
    protected void subscribeActual(final SingleObserver<? super T> s) {
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s, source);
        scheduler.scheduleDirect(parent);
    }
 }

scheduler.scheduleDirect(parent)实际的调用相当于scheduler.createWorker().schedule(Runnable{需要扔进新线程的代码}),

worker.schedule()内部使用其线程池worker.executor.submit(Task(runnable))

Scheduler的不同类型

Schedulers提供了适用不同场景的调度器:

  • Schedulers.newThread():创建的每个worker都有个独立的线程池。线程池corePoolSize为1。

  • Schedulers.computation():适用CPU计算操作线程,限定了最大worker数量,数量为处理器个数。预先生成固定数量PoolWorker。每个PoolWorker相当于一个newThreadWorker。新任务会依次加入到PoolWorker中,不会新创建worker,

  • Schedulers.io():适用io操作线程,创建的worker数量并没有上限,但优先从缓存worker取。完成任务的worker会进行缓存,不会立即回收会有个失效时间,定期删除。

  • Schedulers.trampoline():当前线程执行,不会立即执行,等前一个任务完成。当前任务入队执行。

  • Schedulers.single():相较于Schedulers.newThread(),single创建的所有worker公用同一个线程池。

  • AndroidSchedulers.mainThread()在Android主线程执行,内部通过Handler实现,也只能通过Handler。

Scheduler的关键点在于:

  • 每个Scheduler.createWorker()的策略
  • 不同Worker中的线程池策略

Schedulers.newThread

每个NewThreadWorker中都有个ScheduledThreadPoolExecutor。此类型的线程池核心线程数为1,且任务队列DelayedWorkQueue为无限队列。 这就意味着始终只用一个线程在跑,新加入任务入队排列。

public final class NewThreadScheduler extends Scheduler {

    @Override
    public Worker createWorker() {
        return new NewThreadWorker(threadFactory);
    }
}
public class NewThreadWorker extends Scheduler.Worker implements Disposable {
    private final ScheduledExecutorService executor;

    public NewThreadWorker(ThreadFactory threadFactory) {
        executor = SchedulerPoolFactory.create(threadFactory);
    }
    public Disposable scheduleDirect(final Runnable run, ...) {
        ScheduledDirectTask task = new ScheduledDirectTask(run);
        Future<?> f = executor.submit(task);
    }
}
public final class SchedulerPoolFactory {
    public static ScheduledExecutorService create(ThreadFactory factory) {
        final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);
    return exec;
}

public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(), threadFactory);

我们可以写段代码试下。可以看到运行结果每次Schedulers.newThread()都会运行在新起线程池的单个线程中。

 Observable.create<Int> { for (i in 1..3) it.onNext(i) }
//Observable.range(1, 3)
    .doOnNext { println("a doOnNext:$it ${Thread.currentThread()}") }
    .observeOn(Schedulers.newThread())
    .subscribe {
        Thread.sleep(100)
        println("a:$it ${Thread.currentThread()}")
    }

Observable.create<Int> { for (i in 1..3) it.onNext(i) }
    .observeOn(Schedulers.newThread())
    .subscribe {
        Thread.sleep(100)
        println("b:$it ${Thread.currentThread()}")
    }

//输出结果,
a doOnNext:1 Thread[main,5,main]
a doOnNext:2 Thread[main,5,main]
a doOnNext:3 Thread[main,5,main]
a:1 Thread[RxNewThreadScheduler-1,5,main]
b:1 Thread[RxNewThreadScheduler-2,5,main]
a:2 Thread[RxNewThreadScheduler-1,5,main]
b:2 Thread[RxNewThreadScheduler-2,5,main]
a:3 Thread[RxNewThreadScheduler-1,5,main]
b:3 Thread[RxNewThreadScheduler-2,5,main]

Schedulers.computation

入口为ComputationScheduler.java,关键词FixedSchedulerPoolEventLoopWorker

先来看EventLoopWorker:

  • EventLoopWorker封装了PoolWorker
  • PoolWorker直接继承自NewThreadWorker,区别在传入的ThreadFactory不一样

再看FixedSchedulerPool:

  • FixedSchedulerPool初始化时,会构建固定数量(Max_Threads)的PoolWorker,保存在PoolWorker[] eventLoops
  • 调用createWorker(),实际会先从pool中,根据数组下标取PoolWorker,同时下标n++。 我们可以总结:
  • 假如你的代码有100处使用到Schedulers.compution(),那他们也只会在这固定数量worker中依次选用。
  • 同时在每个worker的线程池中,也是单线程排队运行。
//availableProcessors在我的电脑上是8
MAX_THREADS = cap(Runtime.getRuntime().availableProcessors(), Integer.getInteger(KEY_MAX_THREADS, 0));

public ComputationScheduler(ThreadFactory threadFactory) {
    this.pool = new AtomicReference<FixedSchedulerPool>(NONE);
    start();
}
@Override
public void start() {
    FixedSchedulerPool update = new FixedSchedulerPool(MAX_THREADS, threadFactory);
    if (!pool.compareAndSet(NONE, update)) {
        update.shutdown();
    }
}
@Override
public Worker createWorker() {
    return new EventLoopWorker(pool.get().getEventLoop());
}
static final class FixedSchedulerPool implements SchedulerMultiWorkerSupport {
    final int cores;

    final PoolWorker[] eventLoops;
    long n;

    FixedSchedulerPool(int maxThreads, ThreadFactory threadFactory) {
        // initialize event loops
        this.cores = maxThreads;
        this.eventLoops = new PoolWorker[maxThreads];
        for (int i = 0; i < maxThreads; i++) {
            this.eventLoops[i] = new PoolWorker(threadFactory);
        }
    }

    public PoolWorker getEventLoop() {
        // simple round robin, improvements to come
        return eventLoops[(int)(n++ % cores)];
    }
}
static final class EventLoopWorker extends Scheduler.Worker {
    private final PoolWorker poolWorker;

    EventLoopWorker(PoolWorker poolWorker) {
        this.poolWorker = poolWorker;
    }
}

static final class PoolWorker extends NewThreadWorker {
    PoolWorker(ThreadFactory threadFactory) {
        super(threadFactory);
    }
}

可以通过下面的代码验证。

  • Schedulers.computation()虽然调用了26次,但打印结果始终只有8个线程
for (i in 'a'..'z') {
    Observable.create<String> { 
            for (j in 1..3) 
                    it.onNext("$i-$j") }
        .observeOn(Schedulers.computation())
        .subscribe {
            Thread.sleep(100)
            println("a:$it ${Thread.currentThread()} ")
        }
}
//部分输出结果
e-1 Thread[RxComputationThreadPool-5,5,main] 
d-1 Thread[RxComputationThreadPool-4,5,main] 
b-1 Thread[RxComputationThreadPool-2,5,main] 
f-1 Thread[RxComputationThreadPool-6,5,main] 
c-1 Thread[RxComputationThreadPool-3,5,main] 
a-1 Thread[RxComputationThreadPool-1,5,main] 
g-1 Thread[RxComputationThreadPool-7,5,main] 
h-1 Thread[RxComputationThreadPool-8,5,main] 
...

Schedulers.io

入口IoScheduler.这里我们关心CachedWorkerPoolEventLoopWorker(区别于上一节) 先来看EventLoopWorker

  • 封装了ThreadWorker,内部的ThreadWorker不是直接创建,而是从CachedWorkerPool中取。
  • ThreadWorker继承自NewThreadWorker,额外增加了expirationTime字段。
private static final long KEEP_ALIVE_TIME = 60;
private static final TimeUnit KEEP_ALIVE_UNIT = TimeUnit.SECONDS;

public IoScheduler(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 Worker createWorker() {
    return new EventLoopWorker(pool.get());
}

static final class EventLoopWorker extends Scheduler.Worker {
    private final CachedWorkerPool pool;
    private final ThreadWorker threadWorker;
    
    EventLoopWorker(CachedWorkerPool pool) {
        this.pool = pool;
        this.threadWorker = pool.get();
    }


    @NonNull
    @Override
    public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
        return threadWorker.scheduleActual(action, delayTime, unit, tasks);
    }
}

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;
    }
}

接下来看CachedWorkerPool

  • threadWorker执行完后,EventLoopWorker调用dispose()方法时,将threadWorker加入expiringWorkerQueue,并根据keepAliveTime(60秒)设置过期时间
  • CachedWorkerPool.get(),优先从expiringWorkerQueue中取ThreadWorker,其次新建.
  • CachedWorkerPool中使用evictorService等,定时清理expiringWorkerQueue中过期的ThreadWorker

总结下:

  • 相较于Schedulers.computation,worker数并不固定。
  • 相较于Schedulers.newThread,每次createWorker(),优先从pool中取,不会每次都新建。
static final class CachedWorkerPool implements Runnable {
    private final long keepAliveTime;
    private final ConcurrentLinkedQueue<ThreadWorker> expiringWorkerQueue;

    //定时清理expiringWorkerQueue中过期的ThreadWorker
    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>();
    }

    ThreadWorker get() {
       
        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执行完后,加入expiringWorkerQueue并根据keepAliveTime设置过期时间
    void release(ThreadWorker threadWorker) {
        // Refresh expire time before putting worker back in pool
        threadWorker.setExpirationTime(now() + keepAliveTime);
        expiringWorkerQueue.offer(threadWorker);
    }
}

我们写代码试试:

  • 这里我们主动在onComplete()中调用disposable?.dispose(),继而内部调用worker.dispose()
  • a~y的打印,使用到了25个线程Thread[RxCachedThreadScheduler-25,5,main],符合预期
  • z延后打印,实际会使用前面已出现的worker ThreadThread[RxCachedThreadScheduler-2,5,main],并没有新创建第26个线程
for (i in 'a'..'z') {
    //z延后打印,看看会不会使用缓存的worker
    if (i == 'z') Thread.sleep(3000)
    Observable.create<String> {
        for (j in 1..3) it.onNext("$i-$j")
        it.onComplete()
    }
        .observeOn(Schedulers.io())
        .subscribe(object : Observer<String> {
            var disposable: Disposable? = null
            override fun onSubscribe(d: Disposable) {disposable = d}
            override fun onNext(t: String) {
                Thread.sleep(100)
                println("$t ${Thread.currentThread()} ")
            }
            override fun onComplete() {disposable?.dispose()}
        })
}

//部分输出结果
w-3 Thread[RxCachedThreadScheduler-23,5,main] 
g-3 Thread[RxCachedThreadScheduler-7,5,main] 
y-3 Thread[RxCachedThreadScheduler-25,5,main] 
q-3 Thread[RxCachedThreadScheduler-17,5,main] 
a-3 Thread[RxCachedThreadScheduler-1,5,main] 
z-1 Thread[RxCachedThreadScheduler-2,5,main] 
z-2 Thread[RxCachedThreadScheduler-2,5,main] 
z-3 Thread[RxCachedThreadScheduler-2,5,main] 

Schedulers.single()

这里我们先试下代码

  • 可以看到知道最后的z-3打印,也与a-1使用相同的线程Thread[RxSingleScheduler-1,5,main]
for (i in 'a'..'z') {
   if (i == 'z') Thread.sleep(3000)
   Observable.create<String> {
       for (j in 1..3) it.onNext("$i-$j")
       it.onComplete()
   }
       .observeOn(Schedulers.single())
       .subscribe {  Thread.sleep(100)
           println("$it ${Thread.currentThread()} ") }
}
//部分输出结果
a-1 Thread[RxSingleScheduler-1,5,main]
....
z-1 Thread[RxSingleScheduler-1,5,main] 
z-2 Thread[RxSingleScheduler-1,5,main] 
z-3 Thread[RxSingleScheduler-1,5,main] 

看下代码:

  • 虽然createWorker()每次都新建ScheduledWorker,但每个ScheduledWorker都公用线程池。
public SingleScheduler(ThreadFactory threadFactory) {
    this.threadFactory = threadFactory;
    executor.lazySet(createExecutor(threadFactory));
}

static ScheduledExecutorService createExecutor(ThreadFactory threadFactory) {
    return SchedulerPoolFactory.create(threadFactory);
}
@Override
public Worker createWorker() {
    return new ScheduledWorker(executor.get());
}

Schedulers.trampoline()

先看下示例: 可以看到输出结果均在Thread[main,5,main] 即示例当前主线程。 TrampolineWorker内部维持一个优先级队列PriorityBlockingQueue<TimedRunnable> queueTimedRunnable排序规则:

  • 首先,根据TimedRunnable的执行时间字段execTime
  • 其次如果execTime相同,则根据count字段,即加入的先后。
for (i in 'a'..'z') {
    if (i == 'z') Thread.sleep(3000)
    Observable.create<String> {
        for (j in 1..3) it.onNext("$i-$j")
        it.onComplete()
    }
        .observeOn(Schedulers.trampoline())
        .subscribe {
            Thread.sleep(100)
            println("$it ${Thread.currentThread()} ")
        }
}

部分输出结果
Thread[main,5,main] 
a-1 Thread[main,5,main] 
...
z-1 Thread[main,5,main] 
z-2 Thread[main,5,main] 
z-3 Thread[main,5,main]