RxJava BackPressureStrategy.DROP未丢任务反而入队及Android线程池咨询
问题分析与解决方案
嘿,这个问题的核心是你混淆了ThreadPoolExecutor的任务提交逻辑和RxJava背压策略的适用场景,我来帮你拆解清楚:
1. 先看ThreadPoolExecutor为什么能实现“未完成时丢弃新任务”
你的线程池配置逻辑很清晰:
val oneAtATimeExecutor = ThreadPoolExecutor( 1, // 核心线程数=最大线程数,永远只有1个工作线程 1, 1L, TimeUnit.SECONDS, SynchronousQueue<Runnable>(), // 无容量的同步队列,必须有空闲线程才能接收任务 ThreadPoolExecutor.DiscardPolicy() // 无法提交时直接丢弃任务 )
它的工作逻辑是:当滚动触发新任务时,如果之前的expensiveHttpCall()还在执行(工作线程处于忙碌状态),新任务无法被SynchronousQueue接收,直接触发DiscardPolicy被丢弃,完全符合你的预期。
2. RxJava的BackPressureStrategy.DROP为什么没生效?
你误解了背压策略的适用场景:
- BackPressureStrategy是用来处理同一个Observable序列内,上游事件发射速度远快于下游消费速度的情况(比如上游每秒发射100个事件,下游每秒只能处理10个),这时候DROP会丢弃下游来不及处理的事件。
- 但你的场景是:每次滚动都创建一个新的Observable并发起订阅——这些是完全独立的订阅任务,RxJava会把它们分别交给调度器(比如
subscribeOn(Schedulers.io())默认用CachedThreadPool)排队执行,背压策略根本不会介入这种多订阅的场景,所以新任务自然会被入队而不是丢弃。
3. 用RxJava实现“未完成时丢弃新任务”的正确方式
要达到和ThreadPoolExecutor一样的效果,你需要自己控制“同一时间只允许一个任务执行,新任务直接丢弃”的逻辑,这里给你一个简单直接的方案:
方案:用原子布尔值做执行闸门
维护一个标记变量,每次发起请求前检查是否已有任务在执行,是的话直接丢弃当前请求:
class MyActivity : AppCompatActivity() { private val isTaskRunning = AtomicBoolean(false) private val api = YourHttpApi() // 你的Http服务实例 fun onRecyclerViewScrolled() { // 尝试将标记设为true,成功则说明之前无任务在执行 if (isTaskRunning.compareAndSet(false, true)) { Single.fromCallable { api.expensiveHttpCall() } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { result -> // 处理接口返回结果 }, { error -> // 处理请求错误 }, { // 任务完成(无论成功/失败),重置标记 isTaskRunning.set(false) } ) } // 如果compareAndSet失败,直接丢弃当前请求,不做任何处理 } }
总结
- ThreadPoolExecutor的DiscardPolicy是针对任务提交阶段的拒绝策略,而RxJava背压是针对同序列事件流的消费速度匹配问题,两者应用场景完全不同。
- 要在RxJava中实现“未完成时丢弃新任务”,需要手动控制任务执行的闸门,而不是依赖背压策略。
内容的提问来源于stack exchange,提问作者shayan
相关产品推荐
相关产品推荐

