使用 Kotlin 进行 Android 编程
by Pierre-Olivier Laurence, Amanda Hinchman-Dominguez, G. Blake Meike, Mike Dunn
第 4 章 Android 中的并发性 安卓系统中的并发性
本作品已使用人工智能进行翻译。欢迎您提供反馈和意见:translation-feedback@oreilly.com
本章并不特别关注 Kotlin。相反,它将介绍围绕并发编程的一些问题,这些问题将在本书的其他章节中讨论。本章还将介绍一些 Android 开发人员已经掌握的管理并发任务的工具。
并发编程素有 "黑暗艺术 "之称:只有自诩为奇才的人才能做到,新手要冒很大风险。当然,编写正确的并发程序是一项相当具有挑战性的工作。因为并发程序中的错误并不总能立即显现,这一点尤其如此。要测试并发错误几乎是不可能的,而且即使知道存在并发错误,要重现它们也非常困难。
担心并发编程危险的开发人员最好记住这三点:
-
除了编程,你每天做的几乎所有事情都是并发的。在并发环境中,你会相处得很好。编程才是奇怪的事情,在编程中,事情是按顺序发生的。
-
如果您正试图了解并发编程所带来的问题,那您就走对了路。 即使对并发性的理解不全面,也总比复制示例代码和掰着手指头算要好。
-
并发编程是 Android 的工作方式。除了最琐碎的 Android 应用程序外,其他任何程序都需要并发执行。我们不妨着手去做,看看它到底是怎么一回事!
在了解具体情况之前,我们先来定义几个术语。
第一个术语是进程。进程就是应用程序可以使用的内存,以及一个或多个执行线程。内存空间属于进程,其他进程无法对其产生影响。1应用程序通常以单个进程的形式运行。
当然,这就引入了第二个术语: 线程。线程是一个指令序列,按顺序一条一条执行。
这就引出了一个术语,它在一定程度上推动了本书的其他部分: 线程安全。如果在多个线程执行一组指令时,线程执行指令的任何可能排序,都不会产生每个线程按一定顺序逐次完整执行代码时无法得到的结果,那么这组指令就是线程安全的。这句话有点难以理解,但它只是说,无论多个线程是同时执行所有代码,还是一个线程一个线程地执行代码,都会产生相同的结果。 这意味着运行程序会产生可预测的结果。
那么,如何使程序具有线程安全性呢?关于这个问题有很多很多的想法。我们想提出一种清晰、相对容易遵循且始终正确的方法。我们将在后面几页阐述这条规则。首先,让我们详细讨论一下线程安全的含义。
螺纹安全
我们已经说过,线程安全的代码在由多个线程同时执行时,不能产生线程一个一个执行时无法产生的结果。 不过,这个定义在实践中用处不大:没有人会去测试所有可能的执行顺序。
也许我们可以通过研究代码线程不安全的一些常见方式来解决这个问题。
线程安全故障可分为几类,其中最重要的两类是原子性和可见性。
原子性
几乎所有开发人员都了解原子性问题。 这段代码不是线程安全的:
fununsafe(){globalVar++}
执行这段代码的多个线程可能会相互干扰。每个执行此代码的线程都可能读取globalVar的相同值,例如 3。每个线程都可能将该值递增为 4,然后每个线程都可能更新globalVar ,使其值为 4。即使有 724 个线程执行了这段代码,当所有线程都执行完毕后,globalVar 的值也可能是 4。
这 724 个线程中的每个线程都不可能串行执行该代码,也不可能让globalVar 最后变成 4。由于并发执行代码的结果可能与串行执行产生的任何可能结果不同,因此根据我们的定义,这段代码不是线程安全的。
为了使代码具有线程安全特性,我们需要将对变量globalVar 的读取、递增和写入操作合并为原子操作。原子操作是指不能被其他线程中断的操作。如果读取、递增和写入操作都是原子操作,那么就不会有两个线程看到 ...