RxJava2.0│线程操作

787 阅读15分钟
原文链接: mp.weixin.qq.com

☝点击上方蓝字,关注我们!

1. 基本介绍

Scheduler是RxJava对线程控制器的一个抽象,RxJava内置多个Scheduler的实现,如下表:

Scheduler

作用

single

使用定长为1的线程池(new Scheduled Thread Pool(1)),重复利用这个线程。

newThread

每次都启用新线程,并在新线程中执行操作。

computation

使用的固定的线程池(Fixed Scheduler Pool),大小为CPU核数,适用于CPU密集型计算。

io

适合I/O操作(读写文件,读写数据库,网络信息交互等)所使用的Scheduler。行为模式和newThread()差不多,区别在于io()的内部实现使用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下,io()比new Thread()更有效率。

trampoline

直接在当前线程运行,如果当前线程有其他任务正在执行,则会先暂停其他任务。

Schedulers.from

将java.util.concurrent.Executor转换成一个调度器实例,即可以自定义一个Executor来作为调度器。

线程调度:

默认情况下,Observable和Observer处于同一线程中,如果想要切换线程,则可以使用subscribeOn()和observeOn()。

运行如下代码:

1private void schedulerTest() { 2    Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() { 3        @Override 4        public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception { 5            Log.i(TAG, "create:" + Thread.currentThread().getName()); 6            observableEmitter.onNext(1); 7            observableEmitter.onComplete(); 8        } 9    });10    observable.subscribeOn(Schedulers.newThread())11            .subscribeOn(Schedulers.io())12            .observeOn(Schedulers.io())13            .map(new Function<Integer, Integer>() {14                @Override15                public Integer apply(@NonNull Integer integer) throws Exception {16                    Log.i(TAG, "map1:" + Thread.currentThread().getName());17                    return integer;18                }19            })20            .observeOn(Schedulers.newThread())21            .map(new Function<Integer, Integer>() {22                @Override23                public Integer apply(@NonNull Integer integer) throws Exception {24                    Log.i(TAG, "map2:" + Thread.currentThread().getName());25                    return integer;26                }27            })28            .observeOn(AndroidSchedulers.mainThread())29            .doOnSubscribe(new Consumer<Disposable>() {30                @Override31                public void accept(@NonNull Disposable disposable) throws Exception {32                    Log.i(TAG, "doOnSubscribe:" + Thread.currentThread().getName());33                }34            })35            .subscribeOn(Schedulers.single())36            .subscribe(new Consumer<Integer>() {37                @Override38                public void accept(@NonNull Integer integer) throws Exception {39                    Log.i(TAG, "subscribe:" + Thread.currentThread().getName());40                }41            });42}

我们先直接看一下运行结果:

在我们使用RxJava的时候,其实是大致知道线程调度的规则:subscribeOn()是影响上游线程调度,且只有最上面一次起作用。observeOn()影响下游线程调度,每次都起作用。而这个规则的具体的实现过程是怎么样的呢?

我们从源码看一下subscribeOn()和observeOn()是如何做线程调度的。

2. 源码解析:subscribeOn()

如示例代码:

subscribeOn(Schedulers.single()):会创建一个SingleScheduler对象:

1public SingleScheduler(ThreadFactory threadFactory) { 2    this.threadFactory = threadFactory; 3    executor.lazySet(createExecutor(threadFactory)); 4} 5 6static ScheduledExecutorService createExecutor(ThreadFactory threadFactory) { 7    return SchedulerPoolFactory.create(threadFactory); 8} 9public static ScheduledExecutorService create(ThreadFactory factory) {10    final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);11    tryPutIntoPool(PURGE_ENABLED, exec);12    return exec;13}

这时,一个single的线程池就已经被创建出来,等待分配任务进行调度。我们从第4节可以知道,在逆向订阅的过程中,会执行到ObservableSubscribeOn的如下代码:

1@Override2public void subscribeActual(final Observer<? super T> s) {3    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);45    s.onSubscribe(parent);67    parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));8}

scheduler会执行scheduleDirect()方法,传了一个SubscribeTask对象,实际上就是对Runnable进行了封装,接下来我们看scheduleDirect都做了什么:

1@NonNull2public Disposable scheduleDirect(@NonNull Runnable run) {3    return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);4}

1@NonNull 2public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { 3    final Worker w = createWorker(); 4 5    final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); 6 7    DisposeTask task = new DisposeTask(decoratedRun, w); 8 9    w.schedule(task, delay, unit);1011    return task;12}

首先创建一个Worker对象,跳过错误检查,将已传入的Runnable对象,封装成一个DisposeTask的Runnable对象,交由Worker处理。参照示例代码,我们在subscribeOn时传入的是一个single的scheduler,在这个时候,会返回一个如下的Worker对象:

1@NonNull2@Override3public Worker createWorker() {4    return new ScheduledWorker(executor.get());5}

之后,Worker会执行schedule()方法,所以我们继续看ScheduledWorker的schedule()方法的执行:

1@NonNull 2@Override 3public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { 4    if (disposed) { 5        return EmptyDisposable.INSTANCE; 6    } 7 8    Runnable decoratedRun = RxJavaPlugins.onSchedule(run); 910    ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, tasks);11    tasks.add(sr);1213    try {14        Future<?> f;15        if (delay <= 0L) {16            f = executor.submit((Callable<Object>)sr);17        } else {18            f = executor.schedule((Callable<Object>)sr, delay, unit);19        }2021        sr.setFuture(f);22    } catch (RejectedExecutionException ex) {23        dispose();24        RxJavaPlugins.onError(ex);25        return EmptyDisposable.INSTANCE;26    }2728    return sr;29}

在这个方法里面,会传入一个Runnable对象,封装成一个SchedulerRunnable对象:

1public ScheduledRunnable(Runnable actual, DisposableContainer parent) {2    super(3);3    this.actual = actual;4    this.lazySet(0, parent);5}

这个actual就是传入的Runnable对象。

然后executor就开始执行Runnable任务了,即在这个时候进行了线程调度,接下来我们看看SchedulerRunnable的run()都做了什么:

1@Override 2public void run() { 3    lazySet(THREAD_INDEX, Thread.currentThread()); 4    try { 5        try { 6            actual.run(); 7        } catch (Throwable e) { 8            // Exceptions.throwIfFatal(e); nowhere to go 9            RxJavaPlugins.onError(e);10        }11    } finally {12        lazySet(THREAD_INDEX, null);13        Object o = get(PARENT_INDEX);14        if (o != PARENT_DISPOSED && compareAndSet(PARENT_INDEX, o, DONE) && o != null) {15            ((DisposableContainer)o).delete(this);16        }1718        for (;;) {19            o = get(FUTURE_INDEX);20            if (o == SYNC_DISPOSED || o == ASYNC_DISPOSED || compareAndSet(FUTURE_INDEX, o, DONE)) {21                break;22            }23        }24    }25}

实际上执行的是传入的Runnable对象的run()方法,即DisposeTask的run():

1@Override 2public void run() { 3    runner = Thread.currentThread(); 4    try { 5        decoratedRun.run(); 6    } finally { 7        dispose(); 8        runner = null; 9    }10}

在run()里执行的是decoratedRun的run()方法,而这个decoratedRun其实就是最早我们传入的Runnable对象,即SubscribeTask对象,我们再看看SubscribeTask的run()做了什么:

1@Override2public void run() {3    source.subscribe(parent);4}

好了,到这儿已经走到了这个逆向订阅的末端,同时我们终于看到了在scheduleDirect中实际上提交给Worker的任务,就是将上游订阅SubscribeOnObserver对象的任务交由指定的线程进行处理。

所以,subscribeOn()的线程调度,实际上是在逆向订阅的过程中进行的,接下来继续由下游到上游订阅,上游到下游执行任务,在遇到下一次的线程调度之前,将都会在指定的线程中执行。接下来,让我们看看ObserveOn()的执行过程;

3. 源码解析:observeOn()

我们从逆向订阅看起:

1@Override 2protected void subscribeActual(Observer<? super T> observer) { 3    if (scheduler instanceof TrampolineScheduler) { 4        source.subscribe(observer); 5    } else { 6        Scheduler.Worker w = scheduler.createWorker(); 7 8        source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize)); 9    }10}

先判断是否为当前线程的Scheduler,如果是,什么都不做直接订阅;如果不是,就创建一个Worker对象,然后进行上游订阅的操作。Worker的原理不再赘述。所以我们发现,在ObserveOn()的逆向订阅过程中,只是创建了对象,但并没有进行线程调度,那么我们可以推测,线程调度的时机,应该是在最后由上至下的任务执行阶段,实际上也确实如此,我们直接看onNext():

1@Override 2public void onNext(T t) { 3    if (done) { 4        return; 5    } 6 7    if (sourceMode != QueueDisposable.ASYNC) { 8        queue.offer(t); 9    }10    schedule();11}121314void schedule() {15    if (getAndIncrement() == 0) {16        worker.schedule(this);17    }18}

同subscribeOn()一样,将任务提交给Worker去执行,在这个过程中进行了线程调度。Worker的执行过程不再赘述,这个this其实就是实现了Runnable接口的ObservableObserveOn对象,而在run()方法中:

1@Override2public void run() {3    if (outputFused) {4        drainFused();5    } else {6        drainNormal();7    }8}

这个outputFused变量在两个连续的Observable都需要线程调度时(比如observeOn().observeOn())有为true的可能,默认为false。我们先跳过这个变量,直接看drainNormal()方法:

1void drainNormal() { 2    int missed = 1; 3 4    final SimpleQueue<T> q = queue; 5    final Observer<? super T> a = actual; 6 7    for (;;) { 8        if (checkTerminated(done, q.isEmpty(), a)) { 9            return;10        }1112        for (;;) {13            boolean d = done;14            T v;1516            try {17                v = q.poll();18            } catch (Throwable ex) {19                Exceptions.throwIfFatal(ex);20                s.dispose();21                q.clear();22                a.onError(ex);23                worker.dispose();24                return;25            }26            boolean empty = v == null;2728            if (checkTerminated(d, empty, a)) {29                return;30            }3132            if (empty) {33                break;34            }3536            a.onNext(v);37        }3839        missed = addAndGet(-missed);40        if (missed == 0) {41            break;42        }43    }44}

这段代码的本质是执行了Observer的onNext()方法,继续往下游。所以observeOn()的线程调度实际上是在任务执行的时候进行的,而接下来上游到下游的任务执行,在遇到下一次用observeOn()进行线程调度之前,将都会在指定的线程中执行。

讲解完subscribeOn()和observeOn()的流程,我们回到示例代码:

4. 示例代码过程说明

我们进行一下分析:

首先在对象创建的过程中,没有线程调度。

在逆向订阅的过程中,subscribeOn()会由下而上起作用:首先会遇到最下面的subscribeOn(Schedulers.single()),然后往上走,doOnSubscribe()指的是在订阅后回调即被执行,所以首先打印出:

然后我们接着往上游走,先遇到subscribeOn(Schedulers.io()),切换至io线程,然后继续往上订阅subscribeOn(Schedulers.newThread()),切换至新线程:再往上走,走到create生成的Observable的订阅流程,至此,逆向订阅的过程完成,开始进行任务执行过程。

首先走到create中的subscribe()方法,这时仍停留在新线程中,打印结果如下:

继续往下走,遇到第一个observeOn(),即observeOn(Schedulers.io()),进行了线程调度,故下游的map1操作执行在该io线程:

继续往下走,遇到第二个observeOn(),即observeOn(Schedulers.newThread()),进行了线程调度,故下游的map2操作执行在该新线程:

继续往下走,遇到第三个observeOn(),即observeOn(Schedulers.mainThread()),进行了线程调度,即subscribe的操作回到主线程:

到此,整个的调度过程也就梳理清楚了。

5. 总结

subscribeOn:通过接收一个Scheduler参数,来指定对数据的处理运行在特定的线程调度器Scheduler上。它影响的是逆向订阅过程中onSubscribe()之后的操作。若多次执行subscribeOn,则只有一次起作用(Observable的创建操作符总是被其之后最近的subscribeOn控制),影响上游线程调度。但是,有一种情况特殊,就是在doOnSubscribe操作符之后调用,可以使doOnSubscribe()在指定的调度器中执行,因为doOnSubscribe()在被订阅后会立即执行,所以它不受上游subscribeOn()的影响。

observeOn:接收一个Scheduler参数,来指定对数据的处理运行在特定的线程调度器Scheduler上。它影响的是任务执行时onNext()之后的操作。若多次执行observeOn,则每次都起作用,线程会一直切换,影响下游线程调度。


狐友技术团队其他精彩文章

Swift之Codable实战技巧

不了解GIF的加载原理?看我就够了!

安卓系统权限,你真的了解吗?

AspectJ在Android中的应用


加入搜狐技术作者天团

千元稿费等你来!

戳这里!☛