向我证明RxJava中的PublishSubject不是线程安全的
声明PublishSubject在RxJava中不是线程安全的。嗯 我试图找到任何一个例子,我试图构造任何一个例子来模拟比赛条件,这会导致不想要的结果。但我不能:(向我证明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不是线程安全的吗?通常,人们会问为什么他们的设置会意外运行和/或崩溃,答案是:因为他们同时调用
主题的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)
中。