在RxJava doOnNext()调用中更改值是否安全?

时间:2018-09-06 16:29:44

标签: java rx-java reactive-programming rx-java2

我正在使用一个代码库,其中有很多类似于以下内容的模式:

getPoJoObservableFromSomewhere() // no guarantees about the threading/scheduling here
  .doOnNext(pojo -> System.out.println("got pojo: " + pojo.toString())
  .doOnNext(pojo -> pojo.setName("my new pojo"))
  .observeOn(newThreadScheduler)
  .flatMap(pojo -> doSomethingWithPojo(pojo)) // no guarantees about the threading/scheduling in doSomethingWithPojo()
  .subscribe(pojo -> System.out.println("Got a pojo: " + pojo.toString()));

或更可能:

Pojo myPojo = new Pojo("old name");
getNameFromSomewhere() // intentionally no guarantee what scheduler or thread this is observed on for this example
   .doOnNext(pojo -> pojo.setName("new name"))
   .flatMap(pojo -> doSomethingWithPojo(pojo)) // again, no guarantees about the threading inside this call
   .subscribe(pojo -> (), error -> System.out.println("Error handling goes here"));

假设Pojo对象是纯数据对象,没有副作用,看起来像:

class Pojo {
     private String name;
     public Pojo(String name) { this.name = name; }
     public String getName() { return name; }
     public void setName(String name) { this.name = name; }
}

此模式是否可以避免细微的线程错误?

我能想到的最大的障碍是记忆障碍,但我还没有足够的专家来忽略某些事情我可能不知道的想法。

例如,是否存在内存屏障,以便在doOnNext()调用中发生突变后,所有写入都将被提交,并被flatMap内部doSomethingWithPojo()中发生的任何事情所吸收? >

简而言之:这些模式安全吗?

(我们的代码库是RxJava 1,但是这个问题同样适用于RxJava 2)

1 个答案:

答案 0 :(得分:0)

取决于上游是否同时发射元素This SO显示了Subject如何以非线程安全方式运行的示例。

    AtomicInteger counter = new AtomicInteger();

    // Thread-safe
    // SerializedSubject<Object, Object> subject = PublishSubject.create().toSerialized();

    // Not Thread Safe
    PublishSubject<Object> subject = PublishSubject.create();

    Action1<Object> print = (x) -> System.out.println(Thread.currentThread().getName() + " " + counter);

    Consumer<Integer> sleep = (s) -> {
        try {
            Thread.sleep(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };

    subject
            .doOnNext(i -> counter.incrementAndGet())
            .doOnNext(i -> counter.decrementAndGet())
            .doOnNext(print)
            .filter(i -> counter.get() != 0)
            .doOnNext(i -> {
                        throw new NullPointerException("Concurrency detected");
                    }
            )
            .subscribe();

    Runnable r = () -> {
        for (int i = 0; i < 100000; i++) {
            sleep.accept(1);
            subject.onNext(i);
        }
    };

    ExecutorService pool = Executors.newFixedThreadPool(2);
    pool.execute(r);
    pool.execute(r);