rxJava调度程序用例

时间:2015-07-07 18:06:43

标签: java multithreading thread-safety rx-java rx-android

在RxJava中有5 different schedulers可供选择:

  
      
  1. immediate():创建并返回一个在当前线程上立即执行工作的调度程序。

  2.   
  3. trampoline():创建并返回一个调度程序,该调度程序对当前工作完成后要执行的当前线程进行排队。

  4.   
  5. newThread():创建并返回一个Scheduler,为每个工作单元创建一个新线程。

  6.   
  7. computation():创建并返回用于计算工作的调度程序。这可以用于事件循环,处理回调和其他计算工作。不要在此调度程序上执行IO绑定的工作。使用调度程序。 io()代替。

  8.   
  9. io():创建并返回一个用于IO绑定工作的调度程序。   该实现由Executor线程池支持,该线程池将根据需要增长。这可用于异步执行阻塞IO。不要在此调度程序上执行计算工作。使用调度程序。计算()代替。

  10.   

问题:

前3个调度程序非常自我解释;但是,我对计算 io 感到有点困惑。

  1. 究竟是什么" IO-bound work"?它用于处理流(java.io)和文件(java.nio.files)吗?它用于数据库查询吗?它是用于下载文件还是访问REST API?
  2. 计算() newThread()有什么不同?是每次计算()调用都在单个(后台)线程而不是新(后台)线程上?
  3. 为什么在进行IO工作时调用 computation()是不好的?
  4. 为什么在进行计算工作时调用 io()是不好的?

3 个答案:

答案 0 :(得分:309)

很棒的问题,我认为文档可以提供更多细节。

  1. io()由一个无限制的线程池支持,是你用于非计算密集型任务的东西,也就是那些不会给CPU带来太多负担的东西。因此,与文件系统的交互,与不同主机上的数据库或服务的交互就是很好的例子。
  2. computation()由有限的线程池支持,其大小等于可用处理器的数量。如果你试图在可用处理器之外并行安排cpu密集型工作(比如使用newThread()),那么当线程争夺处理器并且它可能是潜在的时候,你就会面临线程创建开销和上下文切换开销表现很好。
  3. 最好离开computation()进行CPU密集型工作,否则您将无法获得良好的CPU利用率。
  4. 由于2中讨论的原因,将io()用于计算工作是不好的。io()是无限制的,如果您在io()上并行安排了一千个计算任务,那么这千个任务中的每一个都将拥有自己的线程并争夺CPU产生的上下文切换成本。

答案 1 :(得分:2)

最重要的一点是,与问题中提到的其他线程池相比, Schedulers.io Schedulers.computation 都是不受限制的线程池支持。如果 Executor 是用 newCachedThreadPool 创建的(无自动回收限制),则 Schedulers.from(Executor)共享此特征。线程池)。

如先前的答复和网络上的多篇文章所充分解释的, Schedulers.io Schedulers.computation 应该谨慎使用,因为它们针对工作类型进行了优化以他们的名字。但是,以我的观点,它们最重要的作用是为响应流提供真正的并发性

与新人的看法相反,反应流不是固有并发的,而是固有异步和顺序的。因此,只有在I / O操作被阻塞时才使用 Schedulers.io (例如:使用阻塞命令,例如Apache IOUtils FileUtils.readFileAsString(...)< / em>)将冻结调用线程,直到操作完成。

在操作过程中使用Java AsynchronousFileChannel(...)之类的异步方法不会阻塞调用线程,因此没有必要使用单独的线程。实际上, Schedulers.io 线程确实不适合异步操作,因为它们不运行事件循环,并且永远不会调用回调。

相同的逻辑适用于数据库访问或远程API调用。如果可以使用异步或反应性API进行调用,请不要使用 Schedulers.io

返回并发。您可能无权访问异步或反应式API来异步或同时执行I / O操作,因此,唯一的选择是在一个单独的线程上分派多个调用。 las,反应流在它们的末端是连续的,但是好消息是 flatMap()运算符可以在其核心引入并发。 >

必须在流结构中构建并发,通常使用 flatMap()运算符。可以配置此功能强大的运算符,以便为您的 flatMap()嵌入式函数内部提供多线程上下文。该上下文由诸如 Scheduler.io Scheduler.computation 之类的多线程调度程序提供。

有关RxJava2 SchedulersConcurrency的文章中找到更多详细信息,您将在其中找到代码示例以及有关如何依次和同时使用Scheduler的详细说明。

希望这会有所帮助,

Softjake

答案 2 :(得分:1)

This blog post provides an excellent answer

摘自博客文章:

Schedulers.io()由无限制的线程池支持。它用于非CPU密集型I / O类型的工作,包括与文件系统的交互,执行网络调用,数据库交互等。该线程池旨在用于异步执行阻塞IO。

Schedulers.computation()由有限制的线程池支持,该线程池的大小取决于可用处理器的数量。它用于计算或CPU密集型工作,例如调整图像大小,处理大型数据集等。请注意:当分配的计算线程多于可用内核时,性能会因上下文切换和线程创建开销而降低,因为线程争夺处理者的时间。

Schedulers.newThread()为计划的每个工作单元创建一个新线程。该调度程序非常昂贵,因为每次都会产生新线程并且不会发生重用。

Schedulers.from(执行程序执行程序)创建并返回由指定执行程序支持的自定义计划程序。若要限制线程池中的并发线程数,请使用Scheduler.from(Executors.newFixedThreadPool(n))。这样可以保证,如果在所有线程都被占用时安排了任务,则该任务将排队。池中的线程将一直存在,直到被明确关闭为止。

RpAndroid扩展库向RxJava提供了

主线程或 AndroidSchedulers.mainThread()。主线程(也称为UI线程)是发生用户交互的地方。应该注意不要使该线程过载,以防止不稳定的无响应UI,或者更糟糕的是,“应用程序无响应”(ANR)对话框。

Schedulers.single()是RxJava 2中的新增功能。该调度程序由单个线程支持,该线程按请求的顺序依次执行任务。

Schedulers.trampoline()通过参与的工作线程之一以FIFO(先进先出)的方式执行任务。通常在实现递归时使用,以避免增长调用堆栈。