Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RxJava2 thread safety

RxJava2 thread safety

Avatar for Nikita.Nemtin

Nikita.Nemtin

March 25, 2019

Other Decks in Programming

Transcript

  1. Why RxJava is not thread safe? • Performance • Implementation

    complexity • Harder to maintain • Harder to reason about operators and the flows they combined to
  2. @Test fun `break distinct()`() { val numberOfThreads = 10 repeat(100000)

    { println("Iteration = $it") val actuallyReceived = AtomicInteger() val source = ObservableSource<Int> { observer -> val threads = mutableListOf<Thread>() val latch = CountDownLatch(numberOfThreads) (0 until numberOfThreads).forEach { threadNum -> threads.add( thread(start = false) { repeat(100) { observer.onNext(threadNum) } latch.countDown() } )
  3. } latch.countDown() } ) } threads.forEach { thread -> thread.start()

    } latch.await() observer.onComplete() } Observable.defer<Int> { source } .distinct() .blockingSubscribe { actuallyReceived.incrementAndGet() } val got = actuallyReceived.get() assertTrue("got $got", actuallyReceived.get() == 10) } }
  4. } latch.countDown() } ) } threads.forEach { thread -> thread.start()

    } latch.await() observer.onComplete() } Observable.defer<Int> { source } .serialize() .distinct() .blockingSubscribe { actuallyReceived.incrementAndGet() } val got = actuallyReceived.get() assertTrue("got $got", actuallyReceived.get() == 10) } }
  5. Subjects • Publish Subject • Behaviour Subject • Replay Subject

    • Async Subject • Serialized subject - the only thread safe.
  6. @Test fun `break PublishSubject`() { val numberOfThreads = 10 repeat(100000)

    { println("Iteration = $it") val publishSubject = PublishSubject.create<Int>() val latch = CountDownLatch(numberOfThreads) val threads = mutableListOf<Thread>() val actuallyReceived = AtomicInteger() val counter = AtomicInteger() publishSubject .doOnNext { counter.incrementAndGet() } .doOnNext { counter.decrementAndGet() } .filter { counter.get() != 0 } .subscribe { actuallyReceived.incrementAndGet() }
  7. .doOnNext { counter.incrementAndGet() } .doOnNext { counter.decrementAndGet() } .filter {

    counter.get() != 0 } .subscribe { actuallyReceived.incrementAndGet() } (0..numberOfThreads).forEach { threads += thread(start = false) { publishSubject.onNext(it) latch.countDown() } } threads.forEach { it.start() } latch.await() val got = actuallyReceived.get() assertTrue("got $got", actuallyReceived.get() == 0) } }
  8. @Test fun `break PublishSubject`() { val numberOfThreads = 10 repeat(100000)

    { println("Iteration = $it") val publishSubject = PublishSubject.create<Int>().toSerialized() val latch = CountDownLatch(numberOfThreads) val threads = mutableListOf<Thread>() val actuallyReceived = AtomicInteger() val counter = AtomicInteger() publishSubject .doOnNext { counter.incrementAndGet() } .doOnNext { counter.decrementAndGet() } .filter { counter.get() != 0 } .subscribe { actuallyReceived.incrementAndGet() }
  9. public void onNext(@NonNull T t) { if (done) { return;

    } if (t == null) { upstream.dispose(); onError(new NullPointerException("onNext called with… return; } synchronized (this) { if (done) { return; } if (emitting) { AppendOnlyLinkedArrayList<Object> q = queue; if (q == null) { q = new AppendOnlyLinkedArrayList<Object>(QUEU… queue = q;
  10. if (done) { return; } if (emitting) { AppendOnlyLinkedArrayList<Object> q

    = queue; if (q == null) { q = new AppendOnlyLinkedArrayList<Object>(QUEU… queue = q; } q.add(NotificationLite.next(t)); return; } emitting = true; } downstream.onNext(t); emitLoop(); }
  11. return; } synchronized (this) { if (done) { return; }

    if (emitting) { AppendOnlyLinkedArrayList<Object> q = queue; if (q == null) { q = new AppendOnlyLinkedArrayList<Object>(QUEU… queue = q; } q.add(NotificationLite.next(t)); return; } emitting = true; } downstream.onNext(t);
  12. void emitLoop() { for (;;) { AppendOnlyLinkedArrayList<Object> q; synchronized (this)

    { q = queue; if (q == null) { emitting = false; return; } queue = null; } if (q.accept(downstream)) { return; } } }
  13. /* public */ final class SerializedSubject<T> extends Subject<… // Some

    code here /** * Constructor that wraps an actual subject. * @param actual the subject wrapped */ SerializedSubject(final Subject<T> actual) { this.actual = actual; }
  14. Summarising the talk • Thread safety markers for rx operators:

    • it operates on several Observables • it has a scheduler as a parameter
  15. Summarising the talk • Thread safety quick sheet • Try

    not to use Observable.create(…) • Remember about Observable contract and happens-before relation
  16. Summarising the talk • Thread safety quick sheet • Try

    not to use Observable.create(…) • Remember about Observable contract and happens-before relation • Use serialize() and toSerialized()
  17. Summarising the talk • Thread safety quick sheet • Try

    not to use Observable.create(…) • Remember about Observable contract and happens-before relation • Use serialize() and toSerialized() • Try not to extract state out of rx chain
  18. Links • RxJava thread safety of operators and subjects artemzin.com/blog/rxjava-thread-safety-of-operators-and-subjects/

    • Java memory model(ru) javaspecialist.ru/2011/06/java-memory-model.html • Bitbucket repo with this talk examples bitbucket.org/nvnemtin/rxjavatalk1/src/master/ • David Karnok's PublishSubject example stackoverflow.com/a/43265441 • Observable contract reactivex.io/documentation/contract.html
  19. Graphics sources • multithreading https://makeagif.com/gif/multithreading-JloXT7 • synched https://tenor.com/view/pitstop-crew-f1-ferrari-gif-3530236 • race

    condition 1 https://g.redditmedia.com/KHaL_YWhyltzNfM1iNkxV8ZJZbpqnvKT_zc_eigPu-Q.gif? w=320&s=2487df64f830af9d8b5ba34d484d7a86 • reordering https://cs4.pikabu.ru/post_img/2016/01/11/8/1452514726180132199.gif • visibility http://suptg.thisisnotatrueending.com/archive/28369334/images/1385080733731.gif • race condition 2 https://www.reddit.com/r/ProgrammerHumor/comments/2skae8/ perfect_demonstration_of_a_race_condition/ • versus https://2ch.hk/b/src/191673522/15504925525871.png
  20. Summarising the talk • Thread safety quick sheet • Try

    not to use Observable.create(…) • Remember about Observable contract and happens-before relation • Use serialize() and toSerialized() • Try not to extract state out of rx chain • Questions? [email protected] @nvnemtin