向我证明RxJava中的PublishSubject不是线程安全的

向我证明RxJava中的PublishSubject不是线程安全的,java,multithreading,thread-safety,rx-java,Java,Multithreading,Thread Safety,Rx Java,声明PublishSubject在RxJava中不是线程安全的。嗯 我试图找到任何一个例子,我试图构造任何一个例子来模拟比赛条件,这会导致不想要的结果。但我不能:( 有人能提供一个例子来证明PublishSubject不是线程安全的吗?通常,人们会问为什么他们的设置会意外运行和/或崩溃,答案是:因为他们同时调用主题的onXXX方法: import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.Ato

声明PublishSubject在RxJava中不是线程安全的。嗯

我试图找到任何一个例子,我试图构造任何一个例子来模拟比赛条件,这会导致不想要的结果。但我不能:(


有人能提供一个例子来证明PublishSubject不是线程安全的吗?

通常,人们会问为什么他们的设置会意外运行和/或崩溃,答案是:因为他们同时调用
主题的onXXX方法

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

import rx.Scheduler.Worker;
import rx.exceptions.MissingBackpressureException;
import rx.observers.AssertableSubscriber;
import rx.schedulers.Schedulers;
import rx.subjects.*;

public class PublishSubjectRaceTest {

    @Test
    public void racy() throws Exception {
        Worker worker = Schedulers.computation().createWorker();
        try {
            for (int i = 0; i < 1000; i++) {
                AtomicInteger wip = new AtomicInteger(2);

                PublishSubject<Integer> ps = PublishSubject.create();

                AssertableSubscriber<Integer> as = ps.test(1);

                CountDownLatch cdl = new CountDownLatch(1);

                worker.schedule(() -> {
                    if (wip.decrementAndGet() != 0) {
                        while (wip.get() != 0) ;
                    }
                    ps.onNext(1);

                    cdl.countDown();
                });
                if (wip.decrementAndGet() != 0) {
                    while (wip.get() != 0) ;
                }
                ps.onNext(1);

                cdl.await();

                as.assertFailure(MissingBackpressureException.class, 1);
            }
        } finally {
            worker.unsubscribe();
        }
    }

    @Test
    public void nonRacy() throws Exception {
        Worker worker = Schedulers.computation().createWorker();
        try {
            for (int i = 0; i < 1000; i++) {
                AtomicInteger wip = new AtomicInteger(2);

                Subject<Integer, Integer> ps = PublishSubject.<Integer>create()
                    .toSerialized();

                AssertableSubscriber<Integer> as = ps.test(1);

                CountDownLatch cdl = new CountDownLatch(1);

                worker.schedule(() -> {
                    if (wip.decrementAndGet() != 0) {
                        while (wip.get() != 0) ;
                    }
                    ps.onNext(1);

                    cdl.countDown();
                });
                if (wip.decrementAndGet() != 0) {
                    while (wip.get() != 0) ;
                }
                ps.onNext(1);

                cdl.await();

                as.assertFailure(MissingBackpressureException.class, 1);
            }
        } finally {
            worker.unsubscribe();
        }
    }
}
import java.util.concurrent.CountDownLatch;
导入java.util.concurrent.AtomicInteger;
导入org.junit.Test;
导入rx.Scheduler.Worker;
导入rx.exceptions.MissingBackpressureException;
导入rx.observators.AssertableSubscriber;
导入rx.schedulers.schedulers;
进口处方。受试者。*;
公开课出版科目考试{
@试验
public void racy()引发异常{
Worker=Schedulers.computation().createWorker();
试一试{
对于(int i=0;i<1000;i++){
AtomicInteger wip=新的AtomicInteger(2);
PublishSubject ps=PublishSubject.create();
AssertableSubscriber as=ps.test(1);
CountDownLatch cdl=新的CountDownLatch(1);
工人计划表(()->{
如果(wip.decrementAndGet()!=0){
while(在制品获取()!=0);
}
ps.onNext(1);
cdl.倒计时();
});
如果(wip.decrementAndGet()!=0){
while(在制品获取()!=0);
}
ps.onNext(1);
cdl.wait();
as.assertFailure(缺少BackpressureException.class,1);
}
}最后{
worker.unsubscribe();
}
}
@试验
public void nonRacy()引发异常{
Worker=Schedulers.computation().createWorker();
试一试{
对于(int i=0;i<1000;i++){
AtomicInteger wip=新的AtomicInteger(2);
Subject ps=PublishSubject.create()
.toSerialized();
AssertableSubscriber as=ps.test(1);
CountDownLatch cdl=新的CountDownLatch(1);
工人计划表(()->{
如果(wip.decrementAndGet()!=0){
while(在制品获取()!=0);
}
ps.onNext(1);
cdl.倒计时();
});
如果(wip.decrementAndGet()!=0){
while(在制品获取()!=0);
}
ps.onNext(1);
cdl.wait();
as.assertFailure(缺少BackpressureException.class,1);
}
}最后{
worker.unsubscribe();
}
}
}

我找到了证据。我认为这个例子比@akarnokd提供的更明显

    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);
AtomicInteger计数器=新的AtomicInteger();
//线程安全
//SerializedSubject subject=PublishSubject.create().toSerialized();
//线程不安全
PublishSubject=PublishSubject.create();
Action1 print=(x)->System.out.println(Thread.currentThread().getName()+“”+计数器);
消费者睡眠=(s)->{
试一试{
睡眠;
}捕捉(中断异常e){
e、 printStackTrace();
}
};
主题
.doOnNext(i->counter.incrementAndGet())
.doOnNext(i->counter.decrementAndGet())
.doOnNext(印刷版)
.filter(i->counter.get()!=0)
.doOnNext(i->{
抛出新的NullPointerException(“检测到并发”);
}
)
.subscribe();
可运行r=()->{
对于(int i=0;i<100000;i++){
睡眠。接受(1);
主题.onNext(i);
}
};
ExecutorService池=Executors.newFixedThreadPool(2);
pool.execute(r);
pool.execute(r);

证明就是文档。哦,谢谢你的链接!!!你太棒了!!!你不需要证明。你需要证明或断言,它是线程安全的。否则它应该被认为是不安全的。@EJP,哇!!!另一个非常有用的评论来自一位专家,他仔细阅读了这个问题!!!你也太棒了!!!我是沃蒂ng以主题外的形式结束这个问题,因为这不是一个问题,这是一个模糊的请求,要求代码重新编写一个bug,该bug可能存在于库的未指定版本中,也可能不存在。我知道toSerialized()可以使主题线程安全,但toSerialized()的权衡是什么?性能?因为我看到每个onXX()内都有双重检查同步块方法。谢谢!toSerialized增加了一些开销,除非您对延迟有严格的限制,否则您的应用程序几乎不会注意到差异。我不理解您的示例。在您的每个案例中,多线程在哪里。所有事情都发生在一个线程中,主题没有订阅。非常不清楚…多线程:
Schedulers.computation().createWorker()
&
worker.schedule(…)
;订阅发生在
ps.test(1)
中。