从源码剖析RxJava基本工作原理(2)

本文深入探讨RxJava中线程切换的实现机制,通过详细分析observeOn操作符,揭示线程切换背后的原理。同时,文章提供了示例代码,帮助读者理解不同线程环境下观察者模式的行为差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RxJava线程切换

我们知道使用rxJava的最重要使用场景就是获取网络请求数据或者是数据库数据等等异步耗时操作的,所以对rxJava线程切换的掌握也十分重要,这一章我们来讲一讲rxJava的线程切换是怎么实现的。

简单demo使用

        Log.e(TAG,"主线程ID:"+Thread.currentThread().getId());
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                Log.e(TAG,"subscribe线程ID:"+Thread.currentThread().getId());
                emitter.onNext(1);
                emitter.onComplete();
            }
        })
        .observeOn(new IoScheduler())
        .subscribe(new Observer<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.e(TAG,"onSubscribe线程ID:"+Thread.currentThread().getId());
            }
            @Override
            public void onNext(Integer integer) {
                Log.e(TAG,"onNext线程ID:"+Thread.currentThread().getId());
            }
            @Override
            public void onComplete() {
                Log.e(TAG,"onComplete线程ID:"+Thread.currentThread().getId());
            }
            @Override
            public void onError(Throwable e) {}
        });

在上一节的代码中插入observeOn(new IoScheduler()) 操作符语句,那么我们看下打印的结果是怎么样的:

09:38:10.462 25232-25232/cn.com.egova.test E/MainActivity: 主线程ID:2
09:38:10.573 25232-25232/cn.com.egova.test E/MainActivity: onSubscribe线程ID:2
09:38:10.573 25232-25232/cn.com.egova.test E/MainActivity: subscribe线程ID:2
09:38:10.577 25232-25354/cn.com.egova.test E/MainActivity: onNext线程ID:4041
09:38:10.577 25232-25354/cn.com.egova.test E/MainActivity: onComplete线程ID:4041

我们可以从打印信息里面知道,observer观察者的onSubscribe()方法是在主线程中的,观察者的onNext和onComplete方法是在相同的子线程中的,而咱们的observable被观察对象的subscribe方法也是在主线程中的。

从observeOn操作符开始分析

那么现在就按照刚刚打印的结果,去源码中查看为什么会是这样的结果,按照惯例点击onObserver操作符,查看它做了什么:

  @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
    	...省略
        return new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize);
    }

就是直接创建一个被观察者对象,参数是刚刚传递进去我们创建的IO.scheduler,这个被观察者的命名很有意思就是ObservableObserveOn,总结所有的被观察者的名称,看出来被观察者的名称组成就是Observable+操作符名称。
我们继续进入这个类的内部看看怎么实现的。

public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;
    final boolean delayError;
    final int bufferSize;
    public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) {
        super(source);
        this.scheduler = scheduler;
        this.delayError = delayError;
        this.bufferSize = bufferSize;
    }
    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        if (scheduler instanceof TrampolineScheduler) {
            ...省略
        } else {
            Scheduler.Worker w = scheduler.createWorker();
            source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
        }
    }

同样的操作,将上游的被观察者作为source成员变量保存,保存操作符中的参数作为scheduler参数。
subscribeActual中的大部分走的是else括号里面的内容,if判断暂时不看,else里面首先创建一个工作执行者worker,然后创建一个观察者对象(也可以看出命名就是,‘操作符+observer’,就是这个操作符创建出来的观察者类)ObserveOnObserver,这个对象负责将上游下发的事件通过自己的onNext()发下给下游,具体是怎么操作的呢,肯定和线程切换相关,要不然我们怎么要分析它呢,具体实现在ObserveOnObserver中,我们去看看代码:

static final class ObserveOnObserver<T> extends BasicIntQueueDisposable<T>
    implements Observer<T>, Runnable {
        private static final long serialVersionUID = 6576896619930983584L;
        final Observer<? super T> downstream;
        final Scheduler.Worker worker;
        SimpleQueue<T> queue;
        Disposable upstream;
        volatile boolean disposed;
        ObserveOnObserver(Observer<? super T> actual, Scheduler.Worker worker, boolean delayError, int bufferSize) {
            this.downstream = actual;
            this.worker = worker;
            ...省略
        }
        @Override
        public void onSubscribe(Disposable d) {
            if (DisposableHelper.validate(this.upstream, d)) {
                this.upstream = d;
                if (d instanceof QueueDisposable) {
                    @SuppressWarnings("unchecked")
                    QueueDisposable<T> qd = (QueueDisposable<T>) d;
                    int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);
                    if (m == QueueDisposable.SYNC) {
                    	...省略
                    }
                    if (m == QueueDisposable.ASYNC) {
                    	...省略
                    }
                }
                queue = new SpscLinkedArrayQueue<T>(bufferSize);
                downstream.onSubscribe(this);
            }
        }
        @Override
        public void onNext(T t) {
            if (done) {
                return;
            }
            if (sourceMode != QueueDisposable.ASYNC) {
                queue.offer(t);
            }
            schedule();
        }

我们在上一章中说到了,不管你的create内的observerOnsubscribe对象如何创建,ObserverOnCreate对象的subscribeActual()方法内的执行顺序是

observableCreate类内							  |ObservableMap类内		     |Observer类内
observer.onSubscribe(parent);					  | --> ObservableMap.onSubscribe| Observer.onSubscribe  
							/emitter.onNext(1);   | --> ObservableMap.onNext	 | --> Observer.onNext
source.subscribe(parent)-->| 					  |                              |
							\emitter.onComplete();| --> ObservableMap.onComplete | --> Observer.onComplete 

所有我们先从onSubscribe()方法看起,我们的实例代码中不会执行到if判断里面去,所以走的是

   queue = new SpscLinkedArrayQueue<T>(bufferSize);
   downstream.onSubscribe(this);

创建了一个队列,然后调用下游的onSubscribe()方法。接下来应该就是onNext()方法的执行了,

    if (sourceMode != QueueDisposable.ASYNC) {
        queue.offer(t);
    }
    schedule();

一看就知道,首先事件入队列,然后调度。我们跟进schedule()方法,看看里面怎么操作的:

	void schedule() {
       if (getAndIncrement() == 0) {
            worker.schedule(this);
        }
    }

让worker去调度ObserveOnObserver,这个类由于是实现了runnable的,所以推理应该是将该任务加入到线程池中去,我们看下ObserveOnObserver的run()方法是如何实现的,

    @Override
    public void run() {
         if (outputFused) {
             drainFused();
         } else {
             drainNormal();
         }
     }

一般情况下还是走的drainNormal()方法,跟进去看下怎么操作的,

 void drainNormal() {
            int missed = 1;
            final SimpleQueue<T> q = queue;
            final Observer<? super T> a = downstream;
            for (;;) {
                if (checkTerminated(done, q.isEmpty(), a)) {
                    return;
                }
                for (;;) {
                    boolean d = done;
                    T v;
                    try {
                        v = q.poll();
                    } catch (Throwable ex) {
                        Exceptions.throwIfFatal(ex);
                        disposed = true;
                        upstream.dispose();
                        q.clear();
                        a.onError(ex);
                        worker.dispose();
                        return;
                    }
                    boolean empty = v == null;
                    if (checkTerminated(d, empty, a)) {
                        return;
                    }
                    if (empty) {
                        break;
                    }
                    a.onNext(v);
                }
                missed = addAndGet(-missed);
                if (missed == 0) {
                    break;
                }
            }
        }

关键就是几句话:for循环(一直从队列中查询出事件), v = q.poll();(从队列中取出事件), a.onNext(v);(将事件发送给下游观察者)。
我们来从这个函数分析一下,各个线程的情况,由于每次onNext都是将上游事件加入到队列中,所以我们可以看出来,observableCreate的内发射的onNext事件是按照代码顺序发射的依次加入到队列中,那么下游的接收到的onNext事件也就是按照顺序来的,保证了顺序的一致性。
drainNormal函数是在runnable中执行的所以,我们可以知道onNext方法执行的线程取决于worker的schedule线程的,我们demo中设置的schedule线程就是io线程,所以要他和主线程不是一个线程。

再来看下onComplete()函数为啥在io线程的,看代码:

    public void onComplete() {
      if (done) {
             return;
         }
         done = true;
         schedule();
     }

代码中走的是先设置为已完成,在进行调度,最后进入的还是drainNormal方法,这个方法中有一个关键函数

     if (checkTerminated(done, q.isEmpty(), a)) {
       	 return;
     }
     boolean checkTerminated(boolean d, boolean empty, Observer<? super T> a) {
            if (disposed) {
            ...省略
            }
            if (d) {
                Throwable e = error;
                if (delayError) {
                ...省略
                } else {
                    if (e != null) {
                    ...省略
                    } else
                    if (empty) {
                        disposed = true;
                        a.onComplete();
                        worker.dispose();
                        return true;
                    }
                }
            }
            return false;
        }

因为标志位done是true,而且onNext事件已经发生完毕,那么队列也是空的最后走到的是if(empty)判断内,直接调用的是下游的onComplete函数,顺便将worker资源释放。那么还是可以得出结论,onComplete函数的所在线程也是worker指定的线程中。

总结

我们可以看到observeOn操作符是封装了下游观察者生成了一个observer的代理者,它的onNext()方法采用了咱们传入进来的schedule来进行调度下游的onNext方法,那么onNext代码调用链就是如下:
在这里插入图片描述
也就是从observerOnObserver操作符开始的onNext以下都是处于调度器所在的线程。

举一反三:假设B,C之间再加上一个D(observerOnServer操作符),那么可以知道C执行onNext方法就应该在D所在的调度器指定的线程中,那么就可以推断出来,下游观察者observer执行onNext的线程取决于上游中离它最近的一个observerOn操作符所指定的线程。

题外话题,QueueDisposable

这个类型的对象我们在observerOnObserver类的onSubscribe()方法里面有看到,而我们的observerOnObserver又刚好是继承自QueueDisposable的,他们的继承关系如下:
我们再来看下各个Observer.onSubscribe()对应的实现,map操作符的看下:
在这里插入图片描述

	public abstract class BasicFuseableObserver<T, R> implements Observer<T>, QueueDisposable<R> {
			  @SuppressWarnings("unchecked")
			    @Override
			    public final void onSubscribe(Disposable d) {
			        if (DisposableHelper.validate(this.upstream, d)) {
			            this.upstream = d;
			            if (d instanceof QueueDisposable) {
			                this.qd = (QueueDisposable<T>)d;
			            }
			            if (beforeDownstream()) {
			                downstream.onSubscribe(this);
			                afterDownstream();
			            }
			        }
			    }

😀忽略掉:beforeDownstream,afterDownstream这两个函数很有意思,后面章节接着会说的。

我们可以看到上面的map操作符的是没有实现onSubscribe函数,而是使用了继承的BasicFuseableObserver类的实现。实现中,就是给下游的观察者传递this作为参数,也就是map本身,而map是继承的disposable的。

我们再看下observerOn操作符的实现,对应的内部类就是ObserveOnObserver,它的实现上面代码已经贴出来了,关键就是这几句:

 public void onSubscribe(Disposable d) {
	 this.upstream = d;
	 if (d instanceof QueueDisposable) {
            @SuppressWarnings("unchecked")
             QueueDisposable<T> qd = (QueueDisposable<T>) d;
             int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);
             if (m == QueueDisposable.SYNC) {
                 sourceMode = m;
                 queue = qd;
                 done = true;
                 downstream.onSubscribe(this);
                 schedule();
                 return;
             }
             if (m == QueueDisposable.ASYNC) {
                 sourceMode = m;
                 queue = qd;
                 downstream.onSubscribe(this);
                 return;
             }
         }

可以看到从参数里面得到disposable,判断其是否是QueueDisposable类型,map操作和observer操作都实现了QueueDisposable的接口,等下我们看下它们是怎么被实现的。
然后调用下面的方法,来判断qd参数是哪种类型的队列,有async(异步),sync(同步),none(无),any(任意)
int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);

例子一

 Observable.create(new ObservableOnSubscribe<Integer>() {
         @Override
         public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
             emitter.onNext(1);
             emitter.onComplete();
         }
     })
     .observeOn(new IoScheduler())

假如是这样的代码的话,disposable是create传递进来的,createEmitter是没有继承QueueDisposable的,因此if判断都不成立,也就是我们开头的那种流程走下去。

例子二

.map(new Function<Integer, Integer>() {
        @Override
        public Integer apply(Integer integer) throws Exception {
            return integer;
        }
    })
    .observeOn(new IoScheduler())

假如代码是这样的话,我们知道map是QueueDisposable类型的,关键是它如何实现requestFusion()方法,在MapObserver中的实现如下:

    @Override
    public int requestFusion(int mode) {
        return transitiveBoundaryFusion(mode);
    }

    protected final int transitiveBoundaryFusion(int mode) {
        QueueDisposable<T> qd = this.qd;
        if (qd != null) {
            if ((mode & BOUNDARY) == 0) {
                int m = qd.requestFusion(mode);
                if (m != NONE) {
                    sourceMode = m;
                }
                return m;
            }
        }
        return NONE;
    }

我们上面已经说过了MapObserver没有复写onSubscribe方法,使用的默认方法,那么this.qd是上游传递下来的qd,上游假设是crete操作符,那么这里map的this.qd也就是空,requestFusion方法返回的就是NONE。对应下面的这句话返回的就是NONE。

int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);

那么上面两个例子的代码执行流程是一样的,if判断都不成立,在observerOnObserver内的onSubscribe()方法里面都是新建一个队列,在执行下游的onSubscribe()方法:

     queue = new SpscLinkedArrayQueue<T>(bufferSize);
     downstream.onSubscribe(this);

例子三

	.observeOn(new IoScheduler())  //A
	.observeOn(new IoScheduler())  //B

假如是这样的话,我们根据上面的经验可以知道,observerOn内部一定会持有一个队列,至于这个队列是自己创建的还是从外部传递进来的,我们分情况看,例子1,2情况下,A操作符会自己创建一个队列给自己使用,再来看看B操作符,

public void onSubscribe(Disposable d) {
     if (DisposableHelper.validate(this.upstream, d)) {
            this.upstream = d;
            if (d instanceof QueueDisposable) {
                @SuppressWarnings("unchecked")
                QueueDisposable<T> qd = (QueueDisposable<T>) d;
				//取决于这句话的返回值
                int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);

                if (m == QueueDisposable.SYNC) {
                    sourceMode = m;
                    queue = qd;
                    done = true;
                    downstream.onSubscribe(this);
                    schedule();
                    return;
                }
                if (m == QueueDisposable.ASYNC) {
                    sourceMode = m;
                    queue = qd;
                    downstream.onSubscribe(this);
                    return;
                }
            }
     int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);

重要的是看下qd.requestFusion是如何实现的,qd参数也就是上游的自己,那么就是ObserverOnObserver类的实现,

        @Override
        public int requestFusion(int mode) {
            if ((mode & ASYNC) != 0) {
                outputFused = true;
                return ASYNC;
            }
            return NONE;
        }

(QueueDisposable.ANY | QueueDisposable.BOUNDARY)& ASYNC一定是不为0的,那么outputFused为true,返回值是ASYNC。
答案出来了,走的是第二个if判断:

      if (m == QueueDisposable.ASYNC) {
                    sourceMode = m;
                    queue = qd;
                    downstream.onSubscribe(this);
                    return;
                }

可以看到,如果两个ObserverOn操作符在一起的话,会共用一个队列,sourceMode=ASYNC,我们想象一下,假设公用的话,那么上面A取出来一个,你B在放进去一个,这不是无穷无尽嘛,所以流程肯定会有变化的,主要是由outputFused标志位控制的。

    @Override
    public void run() {
         if (outputFused) {
             drainFused();
         } else {
             drainNormal();
         }
     }

worker.schedule()这个方法,就会走到run方法,走的就是drainFused()方法,记住

int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);

被B调用,那么A内部的outputFused为true,A走的是 drainFused(); B还是正常走 drainNormal();方法。

  void drainFused() {
   int missed = 1;
       for (;;) {
       		...省略
           downstream.onNext(null);
           ...省略
           missed = addAndGet(-missed);
           if (missed == 0) {
               break;
           }
       }
   }
        
   @Override
   public void onNext(T t) {
       if (done) {
           return;
       }

       if (sourceMode != QueueDisposable.ASYNC) {
           queue.offer(t);
       }
       schedule();
   }

就是不断for循环,直到missed == 0那么就返回,A调用的是了下游B的的onNext,下游中if判断都不成立,直接调用的 schedule()=>run()=>drainNormal=>for循环不断的从队列中取出数据,将事件下发给下游观察者,并且到队列为空的时候就结束for循环。因为有队列的存在,那么事件下发是有序的,观察者消费事件的顺序也是有序的。

总结:多个异步操作符,下游的操作符会复用上游的队列。

下一节,我将讲解subscribeOn操作符,以及总结该操作符影响的范围。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值