Flow中的buffer详解

什么是buffer

  • 上游(emit)下游(collect) 之间插入一个有界/无界缓冲队列(底层是 Channel),把两端解耦:上游可以先"塞进队列就走",下游慢慢消费。
  • 主要用于背压处理并发解耦
scss 复制代码
[ emitter ] --(buffer queue)--> [ collector ]

基本用法与参数

ini 复制代码
flow.buffer(
    capacity: Int = Channel.BUFFERED,          // 常用:Channel.BUFFERED(≈64) / UNLIMITED / RENDEZVOUS(0)
    onBufferOverflow: BufferOverflow = SUSPEND // 也可:DROP_OLDEST / DROP_LATEST
)
  • SUSPEND(默认) :队列满了就挂起上游,保证不丢数据。

  • DROP_OLDEST:丢最老的,保最新的一批("滑窗")。

  • DROP_LATEST:丢最新的(更少见)。

  • capacity = 0(RENDEZVOUS):相当于无缓冲,交接点对点同步,不解耦。

  • UNLIMITED:不建议轻易用,可能吃内存。

和其它操作符的区别

  • conflate() ≈ buffer(1, DROP_OLDEST):永远只保"最新值",中间值丢弃,适合"只关心最新状态"的 UI。

  • collectLatest / mapLatest :来了新值会取消前一个下游处理(不是丢缓存,而是取消处理过程),适合"处理很重但只要最新"的场景(比如渲染)。

  • buffer 不会取消处理,只影响排队与丢弃策略

典型场景

  • 上游快、下游慢:日志、传感器、网络流 → buffer(64, SUSPEND)。

  • 只关心最新:搜索框联想、进度流 → conflate() 或 buffer(1, DROP_OLDEST)。

  • 限制突发 + 保持近实时:buffer(n, DROP_OLDEST)(比如 n=8/16)。

与flowOn的关系

  • flowOn(Dispatchers.IO) 自带一个缓冲边界(类似 buffer(Channel.BUFFERED)),使上下文切换处天然解耦。
  • 需要更深队列时,可在 flowOn 之后再加一个 buffer(k) 增大吞吐。
scss 复制代码
upstream
  .map { ioHeavy() }
  .flowOn(Dispatchers.IO) // 这里已形成一个缓冲边界
  .buffer(32)             // 再加一段队列
  .collect { ui() }

放置位置(很关键)

把 buffer() 放在慢操作之前,切开阻塞链条:

scss 复制代码
flow
  .map { fast() }
  .buffer(64)     // 切断:避免后面的慢步骤"顶住"前面
  .map { slow() }
  .collect { consume() }

与 SharedFlow/StateFlow

  • 它们自身有 replay/extraBufferCapacity/onBufferOverflow 。若再套 buffer(),会再多一层队列,只有在确实需要二级缓冲/隔离时才加。

小示例:对比分离前后

scss 复制代码
val f = flow {
  repeat(100) { i ->
    emit(i)             // 生产很快
    delay(10)
  }
}

// 无 buffer:下游慢会直接"顶住"上游
f.collect { delay(50); println(it) }

// 有 buffer:先排队,上游更流畅(满了再按策略处理)
f.buffer(32, onBufferOverflow = BufferOverflow.SUSPEND)
 .collect { delay(50); println(it) }

选择容量的小经验

  • 先用默认(≈64),观察队列积压内存(可埋点 length/max)。

  • 若突发明显且不想丢数据:升大一点,如 128/256。

  • 只要最新:conflate() 或 buffer(1, DROP_OLDEST)。

一句话总结

buffer 用"队列"把快上游与慢下游隔离;SUSPEND 保正确性,DROP_OLDEST 保实时性;把 buffer 放在慢步骤之前最有效。

相关推荐
狂炫冰美式7 小时前
前端实时推送 & WebSocket 面试题(2026版)
前端·http·面试
Chan1610 小时前
Java 集合面试核心:ArrayList/LinkedList 底层数据结构,HashMap扩容机制详解
java·数据结构·spring boot·面试·intellij-idea
yunxi_0512 小时前
分布式文件服务实战稿:从本地存储到对象存储的架构升级
后端·面试
Chan1613 小时前
【 Java八股文面试 | Redis篇 缓存问题、持久化、分布式锁 】
java·数据库·redis·后端·spring·缓存·面试
zhishidi15 小时前
大模型个性化推荐面试指南
人工智能·面试
xiaoxue..20 小时前
深入理解 JavaScript 异步编程:从单线程到 Promise 的完整指南
前端·javascript·面试·node.js
Croa-vo1 天前
PayPal OA 全流程复盘|题型体验 + 成绩反馈 + 通关经验
数据结构·经验分享·算法·面试·职场和发展
一二爱上蜜桃猫1 天前
2025年(26届)末九计算机拔尖班保研回忆录(清软+软微+上交+科大+AILab+计算所+武大+空天院)
面试
爱学测试的雨果2 天前
软件测试面试题总结【含答案】
功能测试·测试工具·面试
Dream it possible!2 天前
LeetCode 面试经典 150_二叉树_二叉树展开为链表(74_114_C++_中等)
c++·leetcode·链表·面试·二叉树