在并发情况下,不推荐使用通常的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. C++头文件问题

    自己定义的头文件必须要用“***.h”系统头文件必须要用<***.h>stdafx.h 必须放在所有头文件的最前面(如果不放,debug版本没有问题:release版本有问题,会报错)

  2. golang方法详解

    Go 语言 类型方法是一种对类型行为的封装 .Go 语言的方法非常纯粹, 可以看作特殊类型的函数,其显式地将对象实例或指针作为函数的第一个参数,并且参数可以自己指定,而不强制要求一定是 this或se ...

  3. Java标识符中常见的命名规则

    标识符:就是给类,接口,方法,变量等起名字.组成规则:A:英文字母大小写B:数字字符C:$和_注意事项:A:不能以数字开头B:不能使Java中的关键字C:Java语言严格区分大小写常见的命名规则:见名 ...

  4. 图解Python的垃圾回收机制

    Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的 ...

  5. java8 流式编程

    为什么需要流式操作 集合API是Java API中最重要的部分.基本上每一个java程序都离不开集合.尽管很重要,但是现有的集合处理在很多方面都无法满足需要. 一个原因是,许多其他的语言或者类库以声明 ...

  6. 2019-2020-1 20199310《Linux内核原理与分析》第二周作业

    1.问题描述 众所周知,计算机是20世纪最伟大的发明之一,计算机是如何工作的呢?本文主要通过计算机的组成结构和工作原理,以及汇编代码工作过程来进行详细叙述. 2.解决过程 2.1 冯·诺依曼体系结构 ...

  7. 通达OA-2017版本漏洞复现

    搭建环境 服务器 虚拟机系统版本:Windows Server 2016. 安装服务端 1.下载安装程序 这里我们下载的是2017版本的通达OA服务端: 2.安装程序 配置服务: 漏洞复现 1.任意文 ...

  8. Window+Protobuf使用说明

    Window+Protobuf使用说明 C++WindowCmakeProtocbuf 介绍 起因 由于项目中要用到二进制存储数据,之前使用的方式是按照字节数依次将数据写入字节流中, 但是这样做起来做 ...

  9. js 实现图片瀑布流效果,可更改配置参数 带完整版解析代码[waterFall.js]

    前言:         本人纯小白一个,有很多地方理解的没有各位大牛那么透彻,如有错误,请各位大牛指出斧正!小弟感激不尽.         本篇文章为您分析一下原生JS实现图片瀑布流效果 页面需求 1 ...

  10. linux rpm包

    rpm包,软件包,程序包,以.rpm结尾的包 我们刚开始安装的Linux系统是最小化安装(minimol),只安装系统,不安装不必要的软件包 刚开始vim,ifconfig,tree等命令都没有,当然 ...