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,关键词FixedSchedulerPool和EventLoopWorker。
先来看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.这里我们关心CachedWorkerPool和EventLoopWorker(区别于上一节)
先来看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 Thread即Thread[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> queue,TimedRunnable排序规则:
- 首先,根据
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]