RxJava2.0文章五 - Backpressure操作符

前言

这一节看一下:Backpressure操作符

1. 正题


上一节,我们说到通过zip操作符,可以把多个上游发送的事件组合起来发送给下游,有一个问题就是:如果水管A发送事件特别快,水管B发送事件特别慢,可能会出现水管A已经发出1000个事件,水管B才发出一个,组合之后A还剩999个,这些事件只能等B发出事件后才可以组合,那么这些事件必须找一个水缸进行保存。如下图所示:


RxJava2.0文章五 - Backpressure操作符_第1张图片
zip添加水缸.png

由上图可知:

  • 蓝色方框就是zip给我们提供的水缸,用于保存每个水管发出的事件,等两个水缸都有事件之后,就分别从水缸中取事件进行组合,当一个水缸是空的时候就处于等待状态;
  • 这个水缸是一个队列,是按顺序保存的;

如果一直往水缸中存数据,会OOM,下边通过示例代码来演示:

    /**
     * 使用for循环无限的给水缸中存储数据,会导致OOM
     */
    public static void demo1(){

        // 创建第一个上游:Observable1
        Observable observable1 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter emitter) throws Exception {
                for (int i = 0; ; i++) {
                    emitter.onNext(i);
                }
            }
        }).subscribeOn(Schedulers.io()) ;   // 让for循环 在子线程中执行

        // 创建第二个上游:Observable2
        Observable observable2 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter emitter) throws Exception {
                emitter.onNext("A");
            }
        }).subscribeOn(Schedulers.io()) ;  // 让onNext()方法 在子线程中执行


        Observable.zip(observable1, observable2, new BiFunction() {
            @Override
            public String apply(Integer integer, String s) throws Exception {
                return integer + s;
            }
        }).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer() {      // 这里是把onNext()、onError()方法分开new(创建),也可以直接new Observer,一次性创建所有回调方法
                    @Override
                    public void accept(String s) throws Exception {
                        Log.e("TAG", "s -> " + s);
                    }
                }, new Consumer() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e("TAG" , "throwable -> " + throwable) ;
                    }
                });
    }

使用for循环往水缸中无限的存储数据,会OOM,如下图:


RxJava2.0文章五 - Backpressure操作符_第2张图片
图片.png

出现这种情况肯定是我们不想看到的,所以这个时候就可以引出Backpressure。

2. Backpressure


Backpressure是为了控制流量,因为水缸存储能力有限,我们必须从源头去解决问题,这个才是最根本的,既然你发的那么快,并且数据量发的那么大,我就限制不让你发的那么快就ok。

情况一:

我们先从单一的Observable分析,代码如下:

    /**
     * 通过单一的Observable分析:Backpressure
     */
    public static void demo2(){
        Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter emitter) throws Exception {
                for (int i = 0; ; i++) {
                    emitter.onNext(i);
                }
            }

        // 这里建立连接之后,new Consumer只是让实现onNext()方法,不用实现其他方法 ;如果想要实现其他方法,就new Observer实现其他所有方法即可
        }).subscribe(new Consumer() {
            @Override
            public void accept(Integer integer) throws Exception {
                Thread.sleep(2000);
                Log.e("TAG" , "integer -> " + integer) ;
            }
        });
    }

运行结果如下:


RxJava2.0文章五 - Backpressure操作符_第3张图片
图片.png

可以看到很平静,因为上游、下游在同一个线程中,也就是说,上游每次调用onNext()方法相当于直接调用 Consumer中的

@Override
public void accept(Integer integer) throws Exception {
    Thread.sleep(2000);
    Log.e("TAG" , "integer -> " + integer) ;
}
情况二:让demo2()中的for循环切换到子线程中执行,然后切换到主线程中执行new Consumer:
/**
     * 让demo2()中的for循环切换到子线程中执行,然后切换到主线程中执行new Consumer:
     */
    public static void demo3(){
        Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter emitter) throws Exception {
                for (int i = 0; ; i++) {
                    emitter.onNext(i);
                }
            }
        }).subscribeOn(Schedulers.io())    // 这里让上边的 for循环在子线程中执行
                .observeOn(AndroidSchedulers.mainThread())  // 然后切换到主线程中,去执行new Consumer中的 accept()方法
                .subscribe(new Consumer() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Thread.sleep(2000);
                        Log.e("TAG" , "integer -> " + integer) ;
                    }
                });
    }

运行结果如下:


RxJava2.0文章五 - Backpressure操作符_第4张图片
图片.png

可以看到,把上游的 for循环放在 子线程中执行,有OOM,内存爆炸。

3. 为什么让上游 for循环 在主线程和子线程中执行相差这么大?


这个涉及到同步、异步问题。

  • 如demo2():当上游和下游在同一个线程中执行,属于同步问题,意思是上游每发射一次事件,必须等待下游处理完之后,才能接着发射下一个事件;

  • 如demo3():给上游添加线程,让上游的for循环在子线程中执行,然后让下游的new Consumer()的 accept()方法在主线程中执行,属于异步问题,这个时候上游发射数据不需要等待下游接收,可以直接发射,因为是异步关系,两个线程不能直接通信,这个时候需要一个水缸,上游可以发送事件到水缸,下游从水缸中取事件。

  • 所以说:如果上游发射事件太快,下游取事件太慢,就会造成水缸迅速装满,然后溢出来,导致OOM;

4. 同步和异步示例图如下


同步示例图:
RxJava2.0文章五 - Backpressure操作符_第5张图片
同步.png
异步示例图:
RxJava2.0文章五 - Backpressure操作符_第6张图片
异步.png

从上图可知:
同步和异步区别在于是否有水缸。

源头找到了,只要有水缸,就会造成上游、下游发射事件不平衡问题,下一节就去解决这个不平衡的问题。

你可能感兴趣的:(RxJava2.0文章五 - Backpressure操作符)