RxJava之并发处理(SerializedSubject)
在并发情况下,不推荐使用通常的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)的更多相关文章
- RxJava(01-介绍与初体验)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51612415 本文出自:[openXu的博客] 目录: 一 简介 二 简单使用 初步探索 代 ...
- 《Android进阶之光》--RxJava实现RxBus
事件总线RxBus,替代EventBus和otto 1)创建RxBus public class RxBus{ private static volatile RxBus rxBus; private ...
- 使用Rxjava自己创建RxBus
https://piercezaifman.com/how-to-make-an-event-bus-with-rxjava-and-rxandroid/ https://lingyunzhu.git ...
- RxJava Subject
Subject Subject可以看成是一个桥梁或者代理,在某些ReactiveX实现中(如RxJava),它同时充当了Observer和Observable的角色.因为它是一个Observer,它可 ...
- Android 使用RxJava实现一个发布/订阅事件总线
1.简单介绍 1.1.发布/订阅事件主要用于网络请求的回调. 事件总线可以使Android各组件之间的通信变得简单,而且可以解耦. 其实RxJava实现事件总线和EventBus比较类似,他们都依据与 ...
- RxJava2实战--第二章 RxJava基础知识
第二章 RxJava基础知识 1. Observable 1.1 RxJava的使用三步骤 创建Observable 创建Observer 使用subscribe()进行订阅 Observable.j ...
- rxjava回调地狱-kotlin协程来帮忙
本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...
- Android性能优化之利用Rxlifecycle解决RxJava内存泄漏
前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所有想着查找一下资料学 ...
- Android消息传递之基于RxJava实现一个EventBus - RxBus
前言: 上篇文章学习了Android事件总线管理开源框架EventBus,EventBus的出现大大降低了开发成本以及开发难度,今天我们就利用目前大红大紫的RxJava来实现一下类似EventBus事 ...
随机推荐
- [linux][nginx] 常用2
出现提示"Starting nginx: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address alre"的错误提示. 这 ...
- 前端以BASE64码的形式上传图片
前端以BASE64码的形式上传图片 一直有一个很苦恼的问题困扰着铁柱兄,每次上传图片的时候前端要写一大堆js,然后后台也要写一堆java代码做处理.于是就在想,有没有简单又方便的方法把图片上传.今天算 ...
- 关于Google下插件SwitchyOmega用法
开启代理后,尽管访问很自由了,但是我的搬瓦工,是有流量限制的.所以,在之前,我开启一会自由访问模式(戏称),然后关一会,用来方便打开国内网站. 是的,我这么坚持了半个月,之后,就崩溃了,太尼玛繁琐了! ...
- linux--配置开发环境 --Apache篇
现在我的的linux服务器上一般都是使用:Apache 和 Nginx 这两种配置. 你现在安装好了,启动了,也无法通过你服务器绑定的网址访问你的网站. 这是你可以通过这个命令查看一下你的80端口: ...
- 替换字符串sql
update [表名] set 字段名 = replace(与前面一样的字段名,'原本内容','想要替换成什么') UPDATE `zjl_III_hei_zlj_20151111`.`ctrl_ne ...
- Django新手十个开发指导
下面是关于Django新手开发中的一些建议,大家可以参考一下~~ 1,不要将项目名称包含在引用代码里 比如你创建了一个名为"project"的项目,包含一个名为"app& ...
- 鸟哥Linux私房菜(基础篇)——第五章:首次登入与在线求助 man page笔记
1.X Winsows与文本模式的切换 ●[Ctrl] + [Alt] + [F1] ~ [F6] :文字接口登入 tty1 ~ tty6 终端机. ●[Ctrl] + [Alt] + ...
- java中Locks的使用
文章目录 Lock和Synchronized Block的区别 Lock interface ReentrantLock ReentrantReadWriteLock StampedLock Cond ...
- 【Linux常见命令】split命令
split - split a file into pieces 按照指定的行数或大小分割文件 语法: split [OPTION]... [INPUT [PREFIX]] Output fixed- ...
- 在独立的 Root 和 Home 硬盘驱动器上安装 Ubuntu
安装 Linux 系统时,可以有两种不同的方式.第一种方式是在一个超快的固态硬盘上进行安装,这样可以保证迅速开机和高速访问数据.第二种方式是在一个较慢但很强大的普通硬盘驱动器上安装,这样的硬盘转速快并 ...