在并发情况下,不推荐使用通常的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. jmeter5.1.1 生成html报告

    1.首先需要准备好 .jmx 脚本 2.修改jmeter.properties文件(把注解去掉,报告中才能展示所需信息) jmeter.save.saveservice.output_format=x ...

  2. Springboot:thymeleaf模板(八)

    存放位置:resources\templates 访问方式:通过Controller请求访问,不可直接访问(相当于web项目的WEB-INF目录) 环境依赖: <!--thymeleaf模板支持 ...

  3. Java集合:ArrayList (JDK1.8 源码解读)

    ArrayList ArrayList几乎是每个java开发者最常用也是最熟悉的集合,看到ArrayList这个名字就知道,它必然是以数组方式实现的集合 关注点 说一下ArrayList的几个特点,也 ...

  4. 《HelloGitHub》第 49 期

    兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ...

  5. ajax后台返回指定的错误码

    js: $.ajax({ type: "POST", url: 'post.php', data: serialNumber + "&getSerialNumbe ...

  6. 设置共享内存大小 【windows】

    hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAG ...

  7. vue + ArcGIS 地图应用系列一:arcgis api本地部署(开发环境)

    封面 1. 下载 ArcGIS API for JavaScript 官网地址: https://developers.arcgis.com/javascript/3/ 下载地址:http://lin ...

  8. slow-log 和bin-log相关参数介绍

    1. slow-log show global status Slow_queries   --------慢查询的次数,即查询的时间超过long_query_time设置的时间(不能修改) 配置文件 ...

  9. sed 和 awk

    sed [选项] 动作 文件 -n #取消默认输出 ,有n必须要有p,有p加了n才不会有默认输出 -i #真正的替换,修改 -r #支持扩展正则 (* [A-z] '|') 内部命令: p #打印 - ...

  10. 《数据分析实战:基于EXCEL和SPSS系列工具的实践》一1.4 数据分析的流程

    本节书摘来华章计算机<数据分析实战:基于EXCEL和SPSS系列工具的实践>一书中的第1章 ,第1.4节,纪贺元 著 更多章节内容可以访问云栖社区"华章计算机"公众号查 ...