Slide 1

Slide 1 text

UNDERSTANDING RXJAVA SCHEDULER RX JA NIGHT #2 2017.06.12 HIROSHI KUROKAWA
 FABLIC, INC.

Slide 2

Slide 2 text

SCHEDULERS ▸ Schedulers.computation() ▸ Schedulers.io() ▸ Schedulers.newThread() ▸ Schedulers.trampoline()

Slide 3

Slide 3 text

What’s the difference?

Slide 4

Slide 4 text

COMPUTATION SCHEDULER ▸ for CPU-bound work ▸ bounded number of workers ▸ could be blocked if the maximum number of threads are being used

Slide 5

Slide 5 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 6

Slide 6 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 7

Slide 7 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 8

Slide 8 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 9

Slide 9 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 10

Slide 10 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 11

Slide 11 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌}

Slide 12

Slide 12 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println("$time [msec]”) // -> ?

Slide 13

Slide 13 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println(“${Runtime.getRuntime().availableProcessors()}") // -> 4 println("$time [msec]”) // -> ?

Slide 14

Slide 14 text

val time = measureTimeMillis { val observers = List(8) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.computation()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println(“${Runtime.getRuntime().availableProcessors()}") // -> 4 println("$time [msec]”) // -> 2156 [msec]

Slide 15

Slide 15 text

TEXT CAVEAT ‣ Keep in mind how much concurrency you need when using computation scheduler

Slide 16

Slide 16 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 17

Slide 17 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 18

Slide 18 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 19

Slide 19 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 20

Slide 20 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 21

Slide 21 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> 2314 [msec]

Slide 22

Slide 22 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌}.subscribeOn(Schedulers.computation()) ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 23

Slide 23 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .subscribeOn(Schedulers.computation()) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌} ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> ?

Slide 24

Slide 24 text

val time = measureTimeMillis { val observer = TestObserver.create() Observable.range(1, 8) .subscribeOn(Schedulers.computation()) .flatMap { Observable.fromCallable { Thread.sleep(1_000L) println(Thread.currentThread()) it ‌} ‌} .subscribe(observer) observer.awaitTerminalEvent() ‌} println("Elapsed time: $time [msec]") // -> 8211 [msec]

Slide 25

Slide 25 text

IO SCHEDULER ▸ for IO-bound work ▸ unbounded number of workers ▸ could cause OOM

Slide 26

Slide 26 text

val time = measureTimeMillis { val observers = List(24) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.io()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println("$time [msec]”) // -> ? [msec]

Slide 27

Slide 27 text

val time = measureTimeMillis { val observers = List(24) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.io()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println("$time [msec]”) // -> ? [msec]

Slide 28

Slide 28 text

val time = measureTimeMillis { val observers = List(24) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.io()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println("$time [msec]”) // -> 1142 [msec]

Slide 29

Slide 29 text

val time = measureTimeMillis { val observers = List(2026) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.io()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println("$time [msec]”) // -> 1142 [msec]

Slide 30

Slide 30 text

val time = measureTimeMillis { val observers = List(2026) { i -> val observer = TestObserver.create() Completable.fromAction { Thread.sleep(1_000L) } .subscribeOn(Schedulers.io()) .doOnComplete { println("Done $i") } .subscribe(observer) observer ‌} observers.forEach { it.awaitTerminalEvent() } ‌} println("$time [msec]”) // -> 1142 [msec] java.lang.OutOfMemoryError: unable to create new native thread

Slide 31

Slide 31 text

NEW SCHEDULER ▸ If io or computation scheduler does not fit ▸ Create a new thread for each worker ▸ Shut down at dispose()

Slide 32

Slide 32 text

Scheduler under the hood "

Slide 33

Slide 33 text

SCHEDULER AND WORKER ▸ Scheduler does not schedule (!) ▸ Scheduler just creates a worker ▸ Worker processes tasks in a FIFO manner

Slide 34

Slide 34 text

WORKER CONTRACT ▸ All methods should be thread-safe. ▸ Execute tasks in FIFO order. ▸ Make best effort to cancel outstanding tasks when unsubscribed. ▸ Unsubscription of a Worker should not affect other Worker instances of the same Scheduler.

Slide 35

Slide 35 text

SCHEDULER API public Worker createWorker() public long now(TimeUnit unit) public void start() public void shutdown() public Disposable scheduleDirect(Runnable run) public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) public Disposable schedulePeriodicallyDirect(Runnable run, long initialDelay, long period, TimeUnit unit)

Slide 36

Slide 36 text

SCHEDULER API public Worker createWorker() public long now(TimeUnit unit) public void start() public void shutdown() public Disposable scheduleDirect(Runnable run) public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) public Disposable schedulePeriodicallyDirect(Runnable run, long initialDelay, long period, TimeUnit unit)

Slide 37

Slide 37 text

WORKER API public Disposable schedule(Runnable run) public Disposable schedule(Runnable run, long delay, TimeUnit unit) public Disposable schedulePeriodically(Runnable run, long initialDelay, final long period, final TimeUnit unit) public long now(TimeUnit unit)

Slide 38

Slide 38 text

TRAMPOLINE VS. IMMEDIATE ▸ Schedulers.immediate() was dismissed in RxJava 2 ▸ Instead, Schedulers.trampoline() is recommended ▸ Why?

Slide 39

Slide 39 text

IMMEDIATE SCHEDULER (RXJAVA 1) val worker = Schedulers.immediate().createWorker() worker.schedule { println("outer task start") worker.schedule { println("inner task start") println("inner task end") } println("outer task end") }     $POUSBDU7JPMBUJPO ˑ  &YFDVUFUBTLTJO'*'0PSEFS˒

Slide 40

Slide 40 text

val worker = Schedulers.trampoline().createWorker() worker.schedule { println("outer task start") worker.schedule { println("inner task start") println("inner task end") ‌} println("outer task end") ‌} TRAMPOLINE SCHEDULER     $POGPSNJOHUPUIFDPOUSBDU#

Slide 41

Slide 41 text

WHEN WORKER IS BORN, WHEN A TASK IS ASSIGNED ▸ A worker is created when a scheduler-related operator is subscribed ▸ delay(), observeOn(), subscribeOn(), etc. ▸ A task is scheduled when an event is emitted to the operator ▸ Events through an operator are serialised (FIFO) when not delayed

Slide 42

Slide 42 text

FURTHER READING ▸ Advanced Reactive Java: Schedulers (part 1~4)
 http://akarnokd.blogspot.jp/2015/05/schedulers- part-1.html ▸ Read source code
 e.g. SingleScheduler, TrampolineScheduler, ExecutorScheduler

Slide 43

Slide 43 text

Thank you! Hiroshi Kurokawa (ࠇ઒ ༸) @hydrakecat https://github.com/hkurokawa https://speakerdeck.com/hkurokawa hydrakecat.hatenablog.jp