以一个常用的操作符 take(n) 为例:
@JvmStatic
fun main(args: Array) {
val numberOfThreads = 10
repeat(1000) {
println(“Iteration = $it”)
val publishSubject = PublishSubject.create()
val actuallyReceived = AtomicInteger()
publishSubject.take(3).subscribe {
actuallyReceived.incrementAndGet()
}
val latch = CountDownLatch(numberOfThreads)
var threads = listOf()
(0…numberOfThreads).forEach {
threads += thread(start = false) {
publishSubject.onNext(it)
latch.countDown()
}
}
threads.forEach { it.start() }
latch.await()
check(actuallyReceived.get() == 3)
}
}
执行上面代码,由于take
的结果不符合预期,总是会异常退出
看一下take的源码:
public final class ObservableTake extends AbstractObservableWithUpstream<T, T> {
final long limit;
public ObservableTake(ObservableSource source, long limit) {
super(source);
this.limit = limit;
}
protected void subscribeActual(Observer<? super T> observer) {
this.source.subscribe(new ObservableTake.TakeObserver(observer, this.limit));
}
static final class TakeObserver implements Observer, Disposable {
final Observer<? super T> downstream;
boolean done;
Disposable upstream;
long remaining;
TakeObserver(Observer<? super T> actual, long limit) {
this.downstream = actual;
this.remaining = limit;
}
public void onNext(T t) {
if (!this.done && this.remaining-- > 0L) {
boolean stop = this.remaining == 0L;
this.downstream.onNext(t);
if (stop) {
this.onComplete();
}
}
}
}
}
果然不出所料,remaining--
没有任何锁操作,无法保证线程安全。
Rx在对Observable的定义中已经明确告诉我们了:
Observables must issue notifications to observers serially (not in parallel). They may issue these notifications from different threads, but there must be a formal happens-before relationship between the notifications.
happens-before relationship 需要我们保证进入stream数据的先后顺序,避免并发行为。根据官方的解释,这样做可以避免一些有锁操作带来的性能下降,因此仅在必要的时候才确保线程安全。
操作符的线程安全
那么哪些操作符是线程安全的呢?
RxJava的操作符种类繁多,一个一个记忆很难,基本上可以按照这个原则区分:
-
操作单个Observable的操作符都不是线程不安全的,例如常用的 take(n)、map()、distinctUntilChanged() 等,但是带有scheduler参数的除外,例如 window(…, scheduler)、debounce(…, scheduler) 等
-
操作多个Observable的操作符是线程安全的,例如 merge()、combineLatest()、zip()
用代码描述大概是这种感觉:
fun operatorThreadSafety() = if (operator.worksWithOneObservable() &&
operator.supportsScheduling == false) {
Operator.NOT_THREAD_SAFE_AND_THAT_IS_OK
} else {
Operator.MOST_LIKELY_THREAD_SAFE
}
相对于操作符的线程安全,个人认为Subject的使用更需要大家注意。常用的Subject都不是线程安全的(SerializedSubject除外),而最容易出现并发操作的场景恰恰是Subject,例如我们经常会使用Subject作为中继器,异步onNext向Subject发射数据。前面take的例子便是这种场景。
更要命的是我们常配合observeOn来进行线程切换,而observeOn本身也并非线程安全的,翻看其源码会发现,observeOn在切线程时使用了一个线程不安全的队列
queue = new SpscArrayQueue(RxRingBuffer.SIZE);
因此,下面的代码在并发环境中必然会发生问题:
@JvmStatic
fun main(args: Array) {
val numberOfThreads = 10000
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:
这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想PDF文档,内容详细,把Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架这些知识点从源码分析到实战应用都讲的简单明了。
由于文档内容过多,篇幅受限,只能截图展示部分
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
单明了。**
由于文档内容过多,篇幅受限,只能截图展示部分
[外链图片转存中…(img-kLQEjx7T-1713598109796)]
[外链图片转存中…(img-Ri9IQoot-1713598109797)]
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!