RxJava——Flowable与背压

Flowable与背压

一、背压示例

java 复制代码
static final class Customer {
    final int id;

    public Customer(int id) {
        this.id = id;
        System.out.println("正在构造 " + id + "Customer");
    }
}

@Test
public void tets() {
    Observable.range(1, 999)
            .map(Customer::new)
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("获取到的Customer的Id为:" + customer.id);
            });
}


正在构造 1Customer
获取到的Customer的Id为:1
正在构造 2Customer
获取到的Customer的Id为:2
正在构造 3Customer
获取到的Customer的Id为:3
正在构造 4Customer
获取到的Customer的Id为:4
正在构造 5Customer
...

可以看到,我们构造了一个对象,然后设定了一个sleep阻塞(表示消费消耗的时间),待当前元素处理完毕后再处理下一个元素(range内部是一个for循环下发元素的过程,

而且整个工作都在一个线程里进行),于是整个过程处理得很慢。接下来,将元素的下发和消费分配到不同的线程中去(此处借用了observeOn中缓存元素的设计,因为只将消费转移到另一个线程中去是不行的,由前面的知识可知,这些元素的消费都在同一个线程里进行,除非我们自定义多个线程的线程池,将每一个元素的消费都放在不同的线程中执行):

java 复制代码
@Test
public void tets() {
    Observable.range(1, 999)
            .map(Customer::new)
            .observeOn(Schedulers.io())
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("获取到的Customer的Id为:" + customer.id);
            });

    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}


正在构造 1Customer
正在构造 2Customer
正在构造 3Customer
...
正在构造 998Customer
正在构造 999Customer
获取到的Customer的Id为:1
获取到的Customer的Id为:2
获取到的Customer的Id为:3
获取到的Customer的Id为:4
...

这里就展现出生产速度大于消费速度的场景了,也正是由于observeOn有SpscLinkedArrayQueue来做缓冲,所以才可以消费。但之前有介绍过一旦缓存数据量很大,很可能会发生OOM,但对于7.2节将要介绍的Flowable来说,它的相关observeOn修补了这个潜在OOM的Bug。

二、引入Flowable

由前面的内容可以看出,为了避免发生OOM,需要在上游做一些减压操作。JDK有一个Semaphore类可以做到减压,但是若我们自行实现减压操作,那就有些不划算。RxJava2为我们提供了类似的解决方案,这就是Flowable,它是从RxJava1.x的Observable中分离出来的,用于支持背压操作。

java 复制代码
@Test
public void testFlowable() {
    Flowable.range(1, 999)
            .map(Customer::new)
            .observeOn(Schedulers.io())
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("获取到的Customer的Id为:" + customer.id);
            });

    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

正在构造 1Customer
正在构造 2Customer
正在构造 3Customer
...
正在构造 127Customer
正在构造 128Customer
获取到的Customer的Id为:1
获取到的Customer的Id为:2
获取到的Customer的Id为:3
...
获取到的Customer的Id为:94
获取到的Customer的Id为:95
获取到的Customer的Id为:96
正在构造 129Customer
正在构造 130Customer
...

可以看到,上面的代码就是存储一段,消费一段,消费到一定程度再存储,其实有些类似于对库存的操作。这里需要注意的是,类似于将Observable替换为Flowable,相应地也要将Observer替换为Subscriber。.

这里为何存储I28个元素,暂不用理会,接着在存储够128个元素后,立即开始往下推送元素。在推送了96个元素之后,再次"蓄水",再次存储够128个元素(即再往库存中添加96个元素)后,接着往下推送96个元素,依此类推。

由此可以看到背压在消费端也有拉取的效果,即此处并不完全是背压效果,同样包括针对背压的流程控制,因为其元素的缓存实现借助了调度器与消费端的请求来共同完成类似的效果,也就是说,我们可以使用RxJava2提供的方案轻松地实现背压,而且其中间操作的用法基本与Observable没多少差别,可以很轻松地过渡过来。

三、Flowable.create

首先,来看一个Demo:

java 复制代码
@Test
public void test_create() {
    Observable<Integer> source = Observable.create(emitter -> {
        for (int i= 0; i <= 100; i++) {
            if (emitter.isDisposed())
                return;
            emitter.onNext(i);
        }
        emitter.onComplete();
    });
    source.observeOn(Schedulers.io())
            .subscribe(
                    System.out::println,
                    Throwable::printStackTrace,
                    () -> System.out.println("Donw!"));

    try {
        Thread.sleep(1000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

当我们添加背压功能时该如何替换,请看如下改变:

java 复制代码
@Test
public void tes_create2() {
    Flowable<Object> objectFlowable = Flowable.create(emitter -> {
        for (int i = 0; i <= 100; i++) {
            if (emitter.isCancelled())
                return;
            emitter.onNext(i);
        }
    }, BackpressureStrategy.BUFFER);
    objectFlowable.observeOn(Schedulers.computation())
            .subscribe(System.out::println);

    try {
        Thread.sleep(1000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

可以看到,这里使用Flowable替换了Observable,使用emitter.isCancelled替换了emitter.isDisposed的条件判断语句,而Flowable.create里也多了一个BackpressureStrategy类型的参数,其他则没什么变化。通过简单的两三个细节上的改变,我们就可以很轻松地实现背压。


从上面的源码可以看到,我们需要传入FlowableOnSubscribe类型的source,.以及一个BackpressureStrategy类型的背压策略。与Observable类似,FlowableOnSubscribe的subscribe方法需要传入一个FlowableEmitter类型的emitter:

再回到subscribeActual里,查看其中的源码,其中可以通过switch语句根据BackpressureStrategy来选择策略。最后在获得相应的emitter后,就可以按照之前的节奏,先执行Subscribert#onSubscribe(emitter),然后产生订阅动作source.subscribe(emitter)。

下面将注意力放到emitter上,为了适配背压,于是就有了FlowableEmitter,在onNext、onError、onComplete上多加入一些控制方法。与ObservableEmitter#isDisposed不同,这里变为io.reactivex.FlowableEmitter#isCancelled,但功能是等价的,而且还有一个全新的核心方法io.reactivex.FlowableEmitter#requested,其用于确定我们需要关注的库存在"蓄满"之后一段时间内推送的元素数量,在下游调用此方法就可以进行控制。

从图中可以看出关于FlowableEmitter的几种实现。其首先有一个SerializedEmitter的实现,然后在BaseEmitter中通过BaseEmitter?#serialize将自身传入并构造一SerializedEmitter对象,这样后面的基于BaseEmitter的实现类也就有了同样的能力,之后派生出适用于各个场景的功能类。在前面讲解JDK Flow API的时候,我们有提及Subscription,此处回顾一下。其实从这个接口内部来看,就是request(long n)和cancel方法,从两个方法的命名就可以看出这个接口的功能是下游按需通过该接口向上游请求自己需要多少资源以及取消请求。


在request(long n)里有BackpressureHelper...add(this,n);语句,这是BaseEmitter的关键所在。请注意,BaseEmitter继承自原子类AtomicLong,之前我们有提到原子类可以管理状态,而这里则用它来控制请求下发数据的数量。BackpressureHelper.add(this,n)的源码如下:

所以,作为FlowableEmitter的实现类,BaseEmitter对requested的实现就是获取自己当前作为原子类角色(如下面的源码所示,BaseEmitter继承自AtomicLong并实现了FlowableEmitter)的状态值。

四、背压策略

4.1、BackpressureStrategy.BUFFER

  • 行为:将无法被下游立即处理的数据项缓存在一个队列(缓冲区)中,等待下游处理。这是最"宽容"的策略。
  • 风险:如果生产者持续过快,而消费者持续过慢,缓冲区会无限增长,最终可能导致 OutOfMemoryError内存溢出。
  • 适用场景:当你知道数据流总量不大,或者你确信内存足够,并且不希望丢失任何数据时。

若我们给Flowable.create传入的第二个参数是BackpressureStrategy.BUFFER,则从

subscribeActual中可以看到,其选择的是BufferAsyncEmitter,这是默认策略。也就是,新

建了一个new BufferAsyncEmitter(t,bufferSize())对象,其中bufferSize的相关源码如下:

可以看到,假如全局设定了x2.buffer-size参数的值而且值大于1,那么就选取我们自定义的值,否则选取默认值128。这就是之前的Demo中会存储128个元素的原因

(FlowableObserveOn...ObserveOnSubscriber类会在调用onSubscribe方法时将暂存元素的有界队列容量设定为我们指定的大小,默认值是l28)。

java 复制代码
@Test
public void testBuffer() {
    Flowable<Object> objectFlowable = Flowable.create(emitter -> {
        for (int i = 0; i <= 1000; i++) {
            if (emitter.isCancelled())
                return;
            new Customer(i);
            emitter.onNext(i);
        }
    }, BackpressureStrategy.BUFFER);
    objectFlowable.doOnNext(s -> System.out.println("Source pushed " + s))
            .observeOn(Schedulers.computation())
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("所获取到的Customer的Id为:" + customer);
            });

    try {
        Thread.sleep(10000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

...
Source pushed 126
正在构造 127Customer
Source pushed 127
正在构造 128Customer
正在构造 129Customer
正在构造 130Customer
...

4.2、BackpressureStrategy.LATEST

  • 行为:与DROP类似,但它会保留最后一个(最新的)无法处理的数据项。当下游准备好时,它得到的是生产者发出的最新数据,而不是按顺序的下一个。这会丢弃中间积压的所有旧数据。
  • 风险:会丢失数据,但保证了消费者总是能拿到最新的状态。
  • 适用场景:状态更新类的数据流,例如UI界面的数据绑定,我们只关心最终状态,中间过程可以忽略。

BackpressureStrategy...LATEST策略的直观体现是,在下游忙于处理的时候,上游只保存最近的一个元素,而在下游消费者有空闲处理能力的时候,会处理最近的那个下发元素,而之前的元素都会被丢弃。

java 复制代码
@Test
public void testLatest() {
    Flowable<Object> objectFlowable = Flowable.create(emitter -> {
        for (int i = 0; i <= 1000; i++) {
            if (emitter.isCancelled())
                return;
            emitter.onNext(i);
        }
    }, BackpressureStrategy.LATEST);
    objectFlowable.doOnNext(s -> System.out.println("Source pushed " + s))
            .observeOn(Schedulers.computation())
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("所获取到的Customer的Id为:" + customer);
            });

    try {
        Thread.sleep(10000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } 
}

Source pushed 0
Source pushed 1
Source pushed 2
...
Source pushed 126
Source pushed 127
所获取到的Customer的Id为:0
所获取到的Customer的Id为:1
...
所获取到的Customer的Id为:95
Source pushed 1000
所获取到的Customer的Id为:96
...
所获取到的Customer的Id为:126
所获取到的Customer的Id为:127
所获取到的Customer的Id为:1000

可以看到,下发元素直接跳到1000,结合前面解读的内容,除去observeOn中临时存储的128个元素,在这个策略下上游生产元素并下发的速度大于下游消耗的速度。在这种情况下,一旦消费不及时,确实会只消费最近接收的内容(observeOn的行为表示临时帮助消费了128个元素,然后将其他元素暂存了起来,但存满后也无能为力)。

4.3、BackpressureStrategy.DROP

  • 行为:如果下游跟不上,就丢弃当前无法处理的数据项,只处理下游能跟得上的最新数据。
  • 风险:数据会丢失。
  • 适用场景:允许数据丢失的场景,例如实时性要求高但允许偶发丢帧的传感器数据采样、或非关键的日志上报。
java 复制代码
@Test
public void testDrop() {
    Flowable<Object> objectFlowable = Flowable.create(emitter -> {
        for (int i = 0; i <= 1000; i++) {
            if (emitter.isCancelled())
                return;
            emitter.onNext(i);
        }
    }, BackpressureStrategy.DROP);
    objectFlowable.doOnNext(s -> System.out.println("Source pushed " + s))
            .observeOn(Schedulers.computation())
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("所获取到的Customer的Id为:" + customer);
            });

    try {
        Thread.sleep(10000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Source pushed 0
Source pushed 1
...
Source pushed 126
Source pushed 127
所获取到的Customer的Id为:0
所获取到的Customer的Id为:1
...
所获取到的Customer的Id为:125
所获取到的Customer的Id为:126
所获取到的Customer的Id为:127

4.4、BackpressureStrategy.ERROR

  • 行为:一旦检测到下游处理不过来(积压发生),立即向上游发出一个 MissingBackpressureException错误信号,并中断整个数据流。
  • 风险:流会因背压问题而终止。
  • 适用场景:用于快速失败,在开发和测试阶段非常有用,可以帮助你及早发现背压设计问题。生产环境需谨慎使用。
java 复制代码
@Test
public void testError() {
    Flowable<Object> objectFlowable = Flowable.create(emitter -> {
        for (int i = 0; i <= 1000; i++) {
            if (emitter.isCancelled())
                return;
            emitter.onNext(i);
        }
    }, BackpressureStrategy.ERROR);
    objectFlowable.doOnNext(s -> System.out.println("Source pushed " + s))
            .observeOn(Schedulers.computation())
            .subscribe(customer -> {
                Thread.sleep(20);
                System.out.println("所获取到的Customer的Id为:" + customer);
            });

    try {
        Thread.sleep(10000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Source pushed 0
Source pushed 1
Source pushed 2
...
Source pushed 127
所获取到的Customer的Id为:0
io.reactivex.exceptions.OnErrorNotImplementedException:

4.5、BackpressureStrategy.MISSING

  • 行为:不实现任何背压策略。上游会按照自己的速度发送所有数据,依赖下游通过其他方式(如使用 onBackpressureBuffer, onBackpressureDrop等操作符)来处理背压。
  • 风险:如果下游没有自行处理背压,很可能导致上游大量数据积压在内存中,引发内存问题。
  • 适用场景:当你计划在数据流管道的其他位置显式地定义背压处理逻辑时。

4.6、总结

策略 数据丢失 内存风险 行为特点
BUFFER​ ​无限缓存,等待消费
DROP​ 是​ 直接丢弃来不及处理的数据
LATEST​ 是​ 丢弃旧数据,只保留最新一个
ERROR​ 是​ 直接抛异常,流终止
MISSING​ 视后续操作而定 高​ 不处理,交由下游

选择建议:

  • 数据完整性要求极高,且流量可控 -> 考虑 BUFFER(但需注意缓冲区大小限制)。
  • 允许丢弃中间状态,只关心最新状态 -> LATEST(如UI刷新)。
  • 允许丢弃数据,且不要求最新 -> DROP(如非关键指标采样)。
  • 希望在背压发生时快速失败并排查 -> ERROR(用于调试)。
  • 希望自定义精细的背压控制 -> MISSING+ 自定义操作符。

五、Observable转化为Flowable

确实在很多情况下,碰到的都是一个没有背压策略的Observable,而我们又有背压策略需求,这时RxJava2给我们提供了一个toFlowable操作,可以很轻松地将一个Observable转化为一个Flowable,看看如下Demo:

java 复制代码
@Test
public void testObservableToFlowable() {
    Observable.range(1, 1000)
            .toFlowable(BackpressureStrategy.BUFFER)
            .observeOn(Schedulers.computation())
            .subscribe(System.out::println);

    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

可以看到,toFlowable操作需要接收一个BackpressureStrategy参数,其内部实现通过前面的学习,应该可以轻松看懂。但需要注意的是,在我们选择BackpressureStrategy.BUFFER策略的时候,要注意防止OOM的出现,总结一下就是上游维护一个无界队列,所以请谨慎用之。

相对地,Flowable同样有一个可以将Flowable转换为Observable的toObservable操作。它使我们可以很轻松地在整个操作过程中混合使用两者,比如flatMap:

java 复制代码
@Test
public void testFlowableToObservable() {
    Flowable<Integer> integerFlowable = Flowable.range(1, 10)
            .subscribeOn(Schedulers.computation());

    Observable.just("Apple", "Orange", "Appla", "Eatla")
            .flatMap(item -> {
               return integerFlowable.map(i -> item.toUpperCase() + "-" + i).toObservable(); 
            })
            .subscribe(System.out::println);

    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

apple-1
apple-2
apple-3
apple-4
apple-5
apple-6
apple-7
apple-8
apple-9
apple-10
orange-1
orange-2
orange-3
orange-4
orange-5
orange-6
orange-7
orange-8
orange-9
orange-10
appla-1
appla-2
appla-3
appla-4
appla-5
appla-6
appla-7
appla-8
appla-9
appla-10
eatla-1
eatla-2
eatla-3
eatla-4
eatla-5
eatla-6
eatla-7
eatla-8
eatla-9
eatla-10

如此,我们就轻松地在类似于这种局部操作中实现了背压策略,但也引出了一个什么时候使用Observable或者Flowable的问题。拿上面的例子来讲,在源下发元素比较多,但不是超大量,而下游处理速度不太理想的情况下,优先选择Flowable,用空间换时间。还有一点就是,Flowable源在调用toObservable之前依然是支持背压的,一旦转换成Observable源,它就会失去这个能力。总之,若已经使用了Flowable,那就尽量保证在这个环境下使用资源,否则半路放弃也是对性能的浪费。

六、onBackpressureXXX实现背压

6.1、onBackpressureBuffer()

默认行为:

  • 创建一个无界缓冲区,缓存所有下游来不及处理的数据。
  • 当下游请求数据时,从缓冲区按顺序取出交付。
  • 如果上游数据持续过快,最终会导致 OutOfMemoryError。

重载方法(用于精细控制):

java 复制代码
// 1. 指定缓冲区容量
.onBackpressureBuffer(100) // 最多缓存100条
// 当缓冲区满时,默认会抛 `MissingBackpressureException`

// 2. 指定容量和缓冲区满时的策略
.onBackpressureBuffer(100, () -> {}, BackpressureOverflowStrategy.ERROR)
// 策略有:ERROR(抛异常), DROP_OLDEST(丢弃最老的), DROP_LATEST(丢弃最新的)
// 第二个参数是一个Runnable,缓冲区满时会被调用(可做日志等)

// 3. 不指定容量,但指定溢出策略
.onBackpressureBuffer(BackpressureOverflowStrategy.DROP_OLDEST)

适用场景:需要保证数据完整、顺序处理,且能预估数据峰值、设置合理缓冲大小的场景。

java 复制代码
@Test
public void testOnBuffer() {
    Flowable.interval(1, TimeUnit.SECONDS)
            .onBackpressureBuffer()
            .observeOn(Schedulers.computation())
            .subscribe(item -> {
                Thread.sleep(20);
                System.out.println("所获取到的值为:" + item);
            });

    try {
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

所获取到的值为:0
所获取到的值为:1
所获取到的值为:2
所获取到的值为:3

6.2、onBackpressureDrop()

行为:

  • 当下游来不及处理时,直接丢弃当前上游发出的数据项。
  • 当下游再次请求数据时,它收到的是上游下一个发出的数据(不是被丢弃的那个)。
  • 不会缓存任何数据。

重载方法:

java 复制代码
// 1. 基础用法
.onBackpressureDrop()
// 2. 可提供一个Consumer,在数据被丢弃时执行(如记录日志)
.onBackpressureDrop(item -> System.out.println("丢弃了: " + item))

适用场景:允许数据丢失,且对实时性要求高,或者数据是"瞬态"的(例如鼠标移动事件、非关键性的心跳包)。丢弃旧数据可以保证系统不被压垮。

java 复制代码
@Test
public void testOnDrop() {
    Flowable.interval(1, TimeUnit.MILLISECONDS)
            .onBackpressureDrop(i -> System.out.println("舍弃元素:" + i))
            .observeOn(Schedulers.computation())
            .subscribe(item -> {
                Thread.sleep(20);
                System.out.println("所获取到的值为:" + item);
            });

    try {
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}


所获取到的值为:0
所获取到的值为:1
所获取到的值为:2
所获取到的值为:3
所获取到的值为:4
舍弃元素:128
舍弃元素:129
...

6.3、onBackpressureLatest()

行为:

  • 当下游来不及处理时,它会丢弃除最后一个(最新)之外的所有积压数据。
  • 当下游请求数据时,它得到的是上游发出的最新一个数据项。
  • 可以看作是 onBackpressureBuffer(1, ()->{}, DROP_OLDEST)的一个特化和优化版本。

适用场景:状态更新类数据流的绝佳选择。例如,UI 控件根据数据源更新,我们只关心最终应该显示什么,中间的过渡状态可以忽略。这避免了不必要的计算和渲染。

java 复制代码
@Test
public void testOnLatest() {
    Flowable.interval(1, TimeUnit.MILLISECONDS)
            .onBackpressureLatest()
            .observeOn(Schedulers.computation())
            .subscribe(item -> {
                Thread.sleep(20);
                System.out.println("所获取到的值为:" + item);
            });

    try {
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

七、Flowable.generate

Flowable.generate是 RxJava 中用于创建安全、支持背压(Backpressure-aware)的数据源的一个核心工厂方法。它通过一个迭代器风格的回调,让您精确地控制数据生成的节奏,完美解决了 Flowable.create在处理背压时的复杂性和潜在危险。

Flowable.generate的核心是 "下游请求驱动上游生产"。它内部维护了一个简单的状态机,只有当下游通过Subscription.request(n)请求数据时,generate中的回调才会被调用,生产出一个数据项并发送出去。这从根本上避免了生产者(上游)速度超过消费者(下游)速度的问题。

java 复制代码
static <T, S> Flowable<T> generate(
    Callable<S> stateSupplier,
    BiFunction<S, Emitter<T>, S> generator,
    Consumer<? super S> stateDisposer
)
  1. stateSupplier: Callable
    • 作用:提供一个初始的状态对象(State)。每次订阅发生时,这个方法都会被调用一次,创建一个全新的状态对象。这确保了每次订阅都是独立的。
    • 示例:() -> 0(状态是初始计数器),() -> new ArrayList<>()(状态是一个列表),() -> connection(状态是一个数据库连接)。
  2. generator: BiFunction<S, Emitter, S>
    • 作用:这是核心生成器。它接收当前状态和 Emitter作为参数。
      • 您可以在 Emitter上调用 onNext(T)至多一次,来发射一个数据项。
      • 然后,您必须返回一个新的状态对象(可以是修改后的原状态,也可以是一个全新的对象)。
      • 当下游再次请求数据时,这个方法会带着您上次返回的新状态被再次调用,如此循环。
    • 关键限制:每次调用 generator函数,只能调用 onNext、onError或 onComplete其中的一个,且最多调用一次! 这保证了生成的节奏完全由下游请求控制。
  3. stateDisposer: Consumer<? super S>(可选)
    • 作用:当流终止(完成或出错)时,用于清理状态资源的回调。例如,如果状态是一个文件句柄或网络连接,在这里应该关闭它。
    • 这是一个可选的参数,也有两个参数的重载版本 generate(stateSupplier, generator)。

示例1:生成一个有限序列(0 到 9)

java 复制代码
Flowable<Integer> flowable = Flowable.generate(
    () -> 0, // 初始状态:计数器从0开始
    (state, emitter) -> {
        if (state < 10) {
            emitter.onNext(state); // 发射当前状态
            return state + 1; // 返回新状态:计数器+1
        } else {
            emitter.onComplete(); // 序列结束,发送完成信号
            return state; // 这里返回什么状态已经不重要了
        }
    }
);
flowable.subscribe(System.out::println); // 输出:0, 1, 2, ..., 9

示例2:读取文件(模拟,需资源清理)

java 复制代码
Flowable<String> lines = Flowable.generate(
    () -> Files.lines(Paths.get("test.txt")).iterator(), // 状态:迭代器
    (iterator, emitter) -> {
        if (iterator.hasNext()) {
            emitter.onNext(iterator.next()); // 发射下一行
        } else {
            emitter.onComplete(); // 文件读完
        }
        return iterator; // 返回同一个迭代器状态
    },
    iterator -> {
        // 可选的清理函数,这里如果是AutoCloseable资源需要关闭
        System.out.println("资源清理");
    }
);

示例3:无限流但受控(例如每秒请求一个)

java 复制代码
Flowable<Long> infiniteButSafe = Flowable.generate(
    () -> 0L,
    (state, emitter) -> {
        emitter.onNext(state);
        return state + 1;
    }
);
// 即使生成器逻辑是"无限"的,下游也可以通过 request(n) 精确控制获取的数量。
infiniteButSafe.take(5).subscribe(System.out::println); // 输出:0, 1, 2, 3, 4
相关推荐
Thanwind1 小时前
大二上结束随笔
java
L-李俊漩2 小时前
Android studio修改gradle路径
android·android studio
我是大猴子2 小时前
Java面经
java·开发语言
Coder_Boy_2 小时前
Java高级_资深_架构岗 核心知识点全解析(模块四:分布式)
java·spring boot·分布式·微服务·设计模式·架构
百锦再2 小时前
Java ForkJoin 框架全面解析:分而治之的并行编程艺术
java·开发语言·spring boot·spring cloud·kafka·tomcat·maven
s_w.h2 小时前
【 C++ 】搜索二叉树
java·开发语言·c++·算法
专注前端30年2 小时前
【Java高并发系统与安全监控】高并发与性能调优实战:JVM+线程池+Redis+分库分表
java·jvm·redis
星火开发设计2 小时前
关联式容器:map 与 multimap 的键值对存储
java·开发语言·数据结构·c++·算法
九狼JIULANG2 小时前
基于Flutter+Riverpod+MVI 实现的跨平台「AI 提示词优化工具」
android·开源·github