在并发情况下,不推荐使用通常的Subject对象,而是推荐使用SerializedSubject,并发时只允许一个线程调用onnext等方法!

官方说明:

When you use an ordinary Subject as a Subscriber, you must take care not to call its Subscriber.onNext method (or its other on methods) from multiple threads, as this could lead to non-serialized calls, which violates the Observable contract and creates an ambiguity in the resulting Subject. 

大致意思是当我们使用普通的Subject,必须要注意不要在多线程情况下调用onNext 方法,这样是违反了Observable 协议并且会导致执行结果返回带有有歧义的值(线程并发导致返回值混淆了)!

To protect a Subject from this danger, you can convert it into a SerializedSubject with code like the following: 

mySafeSubject = new SerializedSubject( myUnsafeSubject );

官方文档说的很明白了,只需要使用SerializedSubject封装原来的

Subject即可!!

测试demo:

public class MultiThread {
public static void main(String[] args) throws InterruptedException { final PublishSubject<Integer> subject = PublishSubject.create(); subject.subscribe(new Action1<Integer>() { @Override
public void call(Integer t) {
System.out.println("======onnext===>value:" + t + ",threadId:" + Thread.currentThread().getId());
} }); final SerializedSubject<Integer, Integer> ser = new SerializedSubject<Integer, Integer>(subject); for (int i = 0; i < 20; i++) {
final int value = i;
new Thread() {
public void run() {
ser.onNext((int) (value * 10000 + Thread.currentThread().getId()));
};
}.start();
} Thread.sleep(2000);
//
// for (int i = 11; i < 20; i++) {
// final int value = i;
// new Thread() {
// public void run() {
// subject.onNext(value);
// };
// }.start();
// } }
}

执行结果:

======onnext===>value:10,threadId:10

======onnext===>value:10011,threadId:10

======onnext===>value:50015,threadId:10

======onnext===>value:40014,threadId:10

======onnext===>value:30013,threadId:10

======onnext===>value:20012,threadId:10

======onnext===>value:70017,threadId:10

======onnext===>value:60016,threadId:10

======onnext===>value:80018,threadId:10

======onnext===>value:100020,threadId:10

======onnext===>value:90019,threadId:10

======onnext===>value:110021,threadId:21

======onnext===>value:130023,threadId:23

======onnext===>value:120022,threadId:23

======onnext===>value:160026,threadId:26

======onnext===>value:150025,threadId:25

======onnext===>value:140024,threadId:25

======onnext===>value:170027,threadId:25

======onnext===>value:180028,threadId:25

======onnext===>value:190029,threadId:25

上面的结果有点晕了,为什么不是在一个线程上呢?和我之前以为的SerializedSubject时将值放在一个线程上然后处理的想法有些出入了!

源码面前了无秘密,SerializedSubject跟进去看看

 public SerializedSubject(final Subject<T, R> actual) {
super(new OnSubscribe<R>() { @Override
public void call(Subscriber<? super R> child) {
actual.unsafeSubscribe(child);
} });
this.actual = actual;
this.observer = new SerializedObserver<T>(actual);
}

其实SerializedSubject的处理是交给了SerializedObserver,继续跟进到SerializedObserver,类注释:

/**
* Enforces single-threaded, serialized, ordered execution of {@link #onNext}, {@link #onCompleted}, and
* {@link #onError}.
* <p>
* When multiple threads are emitting and/or notifying they will be serialized by:
* </p><ul>
* <li>Allowing only one thread at a time to emit</li>
* <li>Adding notifications to a queue if another thread is already emitting</li>
* <li>Not holding any locks or blocking any threads while emitting</li>
* </ul>
*
* @param <T>
* the type of items expected to be observed by the {@code Observer}
*/

这里一看就明白了,他是只保证同时只有一个线程调用 {@link #onNext}, {@link #onCompleted}, and{@link #onError}.方法,并不是将所有emit的值放到一个线程上然后处理,这就解释了为什么执行结果不是全部在一个线程上的原因 了!

再看看源码再onnext方法:

  @Override
public void onNext(T t) {
FastList list; //同步锁
synchronized (this) {
if (terminated) {
return;
}
if (emitting) {
if (queue == null) {
queue = new FastList();
}
queue.add(t != null ? t : NULL_SENTINEL);
// another thread is emitting so we add to the queue and return
return;
}
// we can emit
emitting = true;
// reference to the list to drain before emitting our value
list = queue;
queue = null;
} // we only get here if we won the right to emit, otherwise we returned in the if(emitting) block above
boolean skipFinal = false;
try {
int iter = MAX_DRAIN_ITERATION;
do {
drainQueue(list);
if (iter == MAX_DRAIN_ITERATION) {
// after the first draining we emit our own value
actual.onNext(t);
}
--iter;
if (iter > 0) {
synchronized (this) {
list = queue;
queue = null;
if (list == null) {
emitting = false;
skipFinal = true;
return;
}
}
}
} while (iter > 0);
} finally {
if (!skipFinal) {
synchronized (this) {
if (terminated) {
list = queue;
queue = null;
} else {
emitting = false;
list = null;
}
}
}
} // this will only drain if terminated (done here outside of synchronized block)
drainQueue(list);
}

// another thread is emitting so we add to the queue and return

如果有其他线程正在处理,则将emit的值放到队列上,线程执行完毕后,会顺序emit队列上的值!!这样就保证了一次只会有一个线程调用!!!

RxJava之并发处理(SerializedSubject)的更多相关文章

  1. RxJava(01-介绍与初体验)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51612415 本文出自:[openXu的博客] 目录: 一 简介 二 简单使用 初步探索 代 ...

  2. 《Android进阶之光》--RxJava实现RxBus

    事件总线RxBus,替代EventBus和otto 1)创建RxBus public class RxBus{ private static volatile RxBus rxBus; private ...

  3. 使用Rxjava自己创建RxBus

    https://piercezaifman.com/how-to-make-an-event-bus-with-rxjava-and-rxandroid/ https://lingyunzhu.git ...

  4. RxJava Subject

    Subject Subject可以看成是一个桥梁或者代理,在某些ReactiveX实现中(如RxJava),它同时充当了Observer和Observable的角色.因为它是一个Observer,它可 ...

  5. Android 使用RxJava实现一个发布/订阅事件总线

    1.简单介绍 1.1.发布/订阅事件主要用于网络请求的回调. 事件总线可以使Android各组件之间的通信变得简单,而且可以解耦. 其实RxJava实现事件总线和EventBus比较类似,他们都依据与 ...

  6. RxJava2实战--第二章 RxJava基础知识

    第二章 RxJava基础知识 1. Observable 1.1 RxJava的使用三步骤 创建Observable 创建Observer 使用subscribe()进行订阅 Observable.j ...

  7. rxjava回调地狱-kotlin协程来帮忙

    本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...

  8. Android性能优化之利用Rxlifecycle解决RxJava内存泄漏

    前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所有想着查找一下资料学 ...

  9. Android消息传递之基于RxJava实现一个EventBus - RxBus

    前言: 上篇文章学习了Android事件总线管理开源框架EventBus,EventBus的出现大大降低了开发成本以及开发难度,今天我们就利用目前大红大紫的RxJava来实现一下类似EventBus事 ...

随机推荐

  1. shiro:自定义remle(二)

    SpringMVC+SpringMVC+Mybatis项目 1:导入相关依赖 <dependencies> <!--测试依赖--> <dependency> < ...

  2. PHP 新特性:如何善用接口与Trait

    首先! 接口也可以继承,通过使用 extends 操作符. 案例: <?php interface a { public function foo(); } interface b extend ...

  3. Lambda表达式最佳实践

    简介 Lambda表达式java 8引入的函数式编程框架.之前的文章中我们也讲过Lambda表达式的基本用法. 本文将会在之前的文章基础上更加详细的讲解Lambda表达式在实际应用中的最佳实践经验. ...

  4. 同步类的基础AbstractQueuedSynchronizer(AQS)

    同步类的基础AbstractQueuedSynchronizer(AQS) 我们之前介绍了很多同步类,比如ReentrantLock,Semaphore, CountDownLatch, Reentr ...

  5. 【手把手教你】win10 虚拟机 VMware Workstation Pro 15下安装Ubuntu 19.04

    虚拟机 VMware Workstation Pro 15.5.0 及永久激活密钥 https://www.cnblogs.com/zero-vic/p/11584437.html Ubuntu19. ...

  6. Bootstrap表格组件 Bootstrap Table

    Bootstrap Table是Bootstrap的一个组件 Bootstrap Table Demo:http://issues.wenzhixin.net.cn/bootstrap-table/i ...

  7. P3842 [TJOI2007]线段

    最近多刷些dp,觉得这个算不上蓝题   在一个\(n\times n\)的平面上,在每一行中有一条线段,第\(i\)行的线段的左端点是\((i, L_i)\),右端点是\((i, R_i)\),其中\ ...

  8. 题目分享I

    题意:2*n的地面,q次操作,每次操作将地面翻转,若该地是地面那翻转就成熔岩,如果是熔岩那翻转就成地面,熔岩人不能走,问人是否能从1,1走到2,n (ps:1,1和2,n不会在翻转的范围内,n,q≤1 ...

  9. 细说 PEP 468: Preserving Keyword Argument Order

    细说 PEP 468: Preserving Keyword Argument Order Python 3.6.0 版本对字典做了优化,新的字典速度更快,占用内存更少,非常神奇.从网上找了资料来看, ...

  10. css的属性选择器

    语法说明: 属性选择器需要将对应属性放入到 方括号中  [ ] ,其中包含属性名,标识符(* $ ~ ^ |) 使用说明: [attribute] 例如  [target] 表示 选择带有 targe ...