协程与互斥锁: Kotlin Mutex的终极指南

2023-12-16

引言

今天我们将深入研究Kotlin中的Mutex(互斥锁)原理以及在实际开发中的使用技巧。Mutex是多线程编程中的关键工具,它可以有效地解决多线程访问共享资源时可能发生的竞态条件问题。

Mutex的基本原理

Mutex是互斥锁的缩写,它是一种同步工具,用于保护共享资源,确保在任何时刻只有一个线程可以访问该资源。在Kotlin中,Mutex是通过 kotlinx.coroutines.sync 包实现的。

Mutex的实现原理

Mutex的实现基于挂起函数和协程的概念。当一个协程请求进入受Mutex保护的临界区时,如果Mutex已经被占用,请求的协程将被挂起,直到Mutex可用。这样可以避免多个协程同时访问共享资源,确保线程安全。

状态变量

Mutex 类的状态变量包括以下两个:

  • owner : 表示锁的拥有者。
  • availablePermits : 表示可用的许可证数量。

初始化

Mutex 类的初始化会将 owner 变量初始化为 NO_OWNER ,表示锁没有被任何线程获取。

获取锁

Mutex 类的 lock() 方法会尝试获取锁。如果锁没有被其他线程获取,则该方法会成功获取锁。如果锁已经被其他线程获取,则该方法会将线程放入到等待队列中,并阻塞线程。

lock() 方法的实现如下:

suspend fun lock(owner: Any?) {
    if (tryLock(owner)) return
    lockSuspend(owner)
}

private suspend fun lockSuspend(owner: Any?) = suspendCancellableCoroutineReusable<Unit> { cont ->
    val contWithOwner = CancellableContinuationWithOwner(cont, owner)
    acquire(contWithOwner)
}

lock() 方法首先会调用 tryLock() 方法尝试获取锁。如果 tryLock() 方法成功,则表示锁没有被其他线程获取, lock() 方法会直接返回。

如果 tryLock() 方法失败,则表示锁已经被其他线程获取。在这种情况下, lock() 方法会调用 lockSuspend() 方法来获取锁。

lockSuspend() 方法会创建一个 CancellableContinuationWithOwner 对象,并将其传递给 acquire() 方法。 acquire() 方法会尝试获取锁。如果成功,则会将 CancellableContinuationWithOwner 对象的 owner 变量设置为 owner 参数。如果失败,则会将 CancellableContinuationWithOwner 对象放入到等待队列中。

释放锁

Mutex 类的 unlock() 方法会释放锁。如果锁的拥有者是当前线程,则该方法会成功释放锁。如果锁的拥有者不是当前线程,则该方法会抛出异常。

unlock() 方法的实现如下:

override fun unlock(owner: Any?) {
    while (true) {
        // Is this mutex locked?
        check(isLocked) { "This mutex is not locked" }
        // Read the owner, waiting until it is set in a spin-loop if required.
        val curOwner = this.owner.value
        if (curOwner === NO_OWNER) continue // <-- ATTENTION, BLOCKING PART HERE
        // Check the owner.
        check(curOwner === owner || owner == null) { "This mutex is locked by $curOwner, but $owner is expected" }
        // Try to clean the owner first. We need to use CAS here to synchronize with concurrent `unlock(..)`-s.
        if (!this.owner.compareAndSet(curOwner, NO_OWNER)) continue
        // Release the semaphore permit at the end.
        release()
        return
    }
}

unlock() 方法首先会检查锁是否已经被获取。如果锁没有被获取,则会抛出异常。

如果锁已经被获取,则会获取锁的拥有者。然后,会检查锁的拥有者是否是当前线程。如果是,则会将锁的拥有者设置为 NO_OWNER ,并释放一个许可证。

其他细节

Mutex 类还提供了以下一些其他细节:

  • holdsLock() 方法用于检查当前线程是否持有锁。
  • tryLock() 方法用于尝试获取锁。如果成功,则会立即返回。如果失败,则会立即返回。
  • onLock 属性用于指定协程在获取锁时要执行的操作。

Mutex 类的实现原理是基于信号量的。 Mutex 类维护了一个 availablePermits 变量,表示可用的许可证数量。如果 availablePermits 变量的值为 0,则表示锁已经被其他线程获取

Mutex的使用技巧

下面我们将介绍在实际开发中使用Mutex的一些技巧,以及注意事项和优化技巧。

如何使用 Mutex 处理特定问题

考虑一个简单的 Android 项目场景,其中有多个协程同时进行网络请求并更新 UI。在这个场景中,我们希望确保网络请求和 UI 更新的顺序正确,避免竞态条件和 UI 不一致的问题。以下是一个使用 Mutex 的示例代码:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex

class MainActivity : AppCompatActivity() {

    private val mutex = Mutex()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 启动多个协程进行网络请求和 UI 更新
        repeat(5) {
            launch {
                performNetworkRequestAndUIUpdate(it)
            }
        }
    }

    private suspend fun performNetworkRequestAndUIUpdate(index: Int) {
        // 模拟网络请求
        delay(1000)

        // 使用 Mutex 保护对 UI 更新的临界区域
        mutex.withLock {
            updateUI("Task $index completed")
        }
    }

    private fun updateUI(message: String) {
        // 在主线程更新 UI
        runOnUiThread {
            textView.append("$message\n")
        }
    }
}

在这个示例中, performNetworkRequestAndUIUpdate 函数模拟了网络请求,然后使用 Mutex 保护了对 UI 更新的临界区域。这样,我们确保了网络请求和 UI 更新的顺序,避免了可能的竞态条件。

Mutex 的作用和效果

保护 UI 更新的临界区域

通过在 performNetworkRequestAndUIUpdate 函数中使用 Mutex,我们确保了对 UI 更新的访问是线程安全的。在任一时刻,只有一个协程能够执行更新操作,避免了多个协程同时修改 UI 导致的问题。

避免竞态条件和数据不一致性

在 Android 中,由于涉及 UI 操作,确保在主线程上按正确的顺序更新 UI 是至关重要的。Mutex 的作用在于协调多个协程对 UI 的访问,避免竞态条件和数据不一致性。

简化异步操作的同步控制

Mutex 提供了一种简单而有效的方式来同步多个协程,特别是在涉及到异步操作(如网络请求)和 UI 更新时。通过在关键区域使用 Mutex,我们可以确保这些操作按照正确的顺序执行,提高了代码的可维护性和稳定性。

注意事项

  1. 协程间互斥 :Mutex主要用于协程之间的互斥,确保同一时间只有一个协程能够访问共享资源,避免竞态条件。
  2. 避免死锁 :在使用Mutex时,要注意避免死锁的情况,即协程获取Mutex后未释放就被挂起,导致其他协程无法继续执行。
  3. 协程取消 :在使用Mutex时,要注意协程的取消情况,确保在协程取消时能够正确释放Mutex,避免资源泄漏。
  4. 性能开销 :过多地使用Mutex可能会导致性能开销,需要谨慎设计代码,避免频繁的互斥操作。

优化技巧

  1. 精细化锁定 :只在需要保护的临界区使用Mutex,避免过多地使用全局的Mutex。
  2. 使用tryLock :在一些情况下,可以使用 tryLock 来尝试获取Mutex,避免协程被挂起,提高执行效率。

结语

通过本文的介绍,相信大家对Kotlin中Mutex的原理和使用有了更深入的了解。在实际开发中,灵活使用Mutex,结合协程的优势,可以更好地处理多线程场景,提高程序的健壮性。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓ (文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

图片

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

协程与互斥锁: Kotlin Mutex的终极指南 的相关文章

  • 如何在android网络库(ION)中使用自签名SSL?

    使用此网络库 https github com koush ion https github com koush ion 由于当前状态是开发 我想使用自签名 SSL 证书 图书馆论坛有一些讨论 https github com koush
  • 使用 dpi 与 dp 缩放图像之间的差异

    我拥有所有由九个补丁位图组成的 dpi 可绘制目录 xxhdpi 和 xxxhdpi 是否必要 可绘制目录中的可绘制资源文件可检索所有缩放的位图 并且我使用可绘制资源文件 现在 我的问题是我还根据大小 小 正常等 创建了 缩放 布局目录 其
  • 从 arraylist 和 hashmap 中删除重复项

    我有一个数组列表 其中包含付款人的姓名 另一个数组列表包含每次付款的费用 例如 nameArray 尼古拉 劳尔 洛伦佐 劳尔 劳尔 洛伦佐 尼古拉 价格数组 24 12 22 18 5 8 1 我需要将每个人的费用相加 所以数组必须变成
  • 需要使用手机后退按钮返回 Web 视图的帮助

    这是我的代码 package com testappmobile import android app Activity import android os Bundle import android view KeyEvent impor
  • 导入已经创建的sqlite数据库(xamarin)

    我正在使用 Xamarin 想知道如何导入我已经创建的 sqlite 数据库 到目前为止 我已将其添加到资产文件夹中 但不知道下一步从哪里开始 string localPath Path Combine System Environment
  • API29 上不推荐使用 setColorFilter

    我使用以下行来更改 VectorDrawable 的颜色 mydrawable getBackground setColorFilter color PorterDuff Mode SRC ATOP 这很好用 尽管它现在已被弃用 文档建议我
  • 如何在 Android TextView 中使用土耳其语字符,如“ş ç ı ö”?

    我想在 android TextView 中写入 ile 但它没有正确绘制 怎样才能使用这样的字符呢 例如 我将文本视图设置为 ile 它显示为 ile 我怎样才能解决这个问题 尝试以下方法 看看是否有帮助 source http grou
  • 如何访问android库项目中的资源

    我正在构建一个 android 库项目 它内部需要一些静态资源 图像 xml 等 然后我想知道我可以把这些资源放在哪里以及如何访问它们 既然我把资源放到了assets文件夹 我使用 AssetManager 来访问资源 public cla
  • android中根据屏幕尺寸计算图像尺寸

    我正在尝试根据屏幕尺寸计算图像高度和宽度 我从后端获取 5 x 7 尺寸的图像 为了将像素乘以 72 进行转换 我有 360 X 504 尺寸的图像 对于 360 X 504 我的动态透明矩形区域将显示为 1 223 x 1 179 即 8
  • Android PhoneGap 插件,UI 选项卡栏,调整 WebView 大小

    我正在创建一个美味的 PhoneGap 插件 希望一旦它能被打开 准备好了 插件基本完成了 我只需要一个漂亮的用户界面 相互作用 简而言之 我想创建一个 本机 android 工具栏组件 如果您实现 PhoneGap UIControls
  • 如何在活动中的必填字段中显示 * 符号

    我需要在活动中的必填字段中显示 符号 你能建议我怎样才能做到这一点吗 任何帮助 将不胜感激 我想说 作为必填字段的标记不遵循本机 Android 主题 的组合setHint and setError对于 Android 应用程序来说看起来更
  • 不变违规:requireNativeComponent:在 UIManager 中找不到“RNSVGSvgViewAndroid”

    我对标题中提到的错误感到头疼 我正在使用react native gifted charts https www npmjs com package react native gifted charts v 1 0 3 https www
  • Android:如何使用后台线程?

    我开发了一个应用程序 它从互联网获取内容并相应地在设备的屏幕上显示它 该程序运行得很好 就是有点慢 加载并显示内容大约需要 3 4 秒 我想将获取内容并将其显示在后台线程中的所有代码放在一起 当程序执行这些功能时 我想显示一个进度对话框 你
  • Android:使 Dialog 周围的所有内容都比默认值更暗

    我有一个具有以下样式的自定义对话框 它显示了一个无边框对话框 后面的任何内容都会 稍微 变暗 我的设计师希望背后的一切都比 Android 的默认设置更暗 但不是完全黑色 有这样的设置吗 我能想到的唯一解决方法是使用全屏活动而不是对话框 只
  • 使用 Android Studio 进行调试永远停留在“等待调试器”状态

    UPDATE The supposed重复是一个关于陷入 等待调试器 执行时Run 而这个问题就陷入了 等待调试器 执行时Debug 产生问题的步骤不同 解决方案也不同 每当我尝试使用Android Studio的调试功能时 运行状态总是停
  • 如何从android中的外部存储中获取所选文件的文件路径?

    我在选择文件的文件路径时遇到问题 我搜索了整个堆栈溢出 但问题没有解决 从设备中选择文件的代码如下所示 Intent intent new Intent Intent ACTION GET CONTENT intent setType in
  • Android 纹理仅显示纯色

    我正在尝试在四边形上显示单个纹理 我有一个可用的 VertexObject 它可以很好地绘制一个正方形 或任何几何对象 现在我尝试扩展它来处理纹理 但纹理不起作用 我只看到一种纯色的四边形 坐标数据位于 arrayList 中 the ve
  • 如何使用应用程序接口将蓝牙套接字传递给另一个活动

    因此 根据我收集的信息 套接字连接既不可序列化 也不可分割 但我需要将蓝牙连接传递给另一个活动 我不想作为中间人编写服务 所以请不要将此作为解决方案发布 我听说有一种方法可以使用自定义应用程序接口来传递这些类型的对象 但我一生都找不到这样的
  • Glass 语音命令给定列表中最接近的匹配项

    使用 Glass 您可以通过 确定 Glass 菜单启动应用程序 它似乎会选择最接近的匹配项 除非命令相距数英里 并且您可以明显看到命令列表 无论如何 是否可以从应用程序内或从语音提示 在初始应用程序触发后 给出类似的列表并返回最接近的匹配
  • Flash 对象未显示在phonegap android 中

    我已经在 android 手机间隙创建了一个应用程序 我有一个屏幕 我想显示一个静态 flash obj 所以我在屏幕 HTML 页面中放入了以下代码

随机推荐

  • 浅析特征增强&个性化在CTR预估中的经典方法和效果对比

    在CTR预估中 主流都采用特征embedding MLP的方式 其中特征非常关键 然而对于相同的特征 在不同的样本中 表征是相同的 这种方式输入到下游模型 会限制模型的表达能力 为了解决这个问题 CTR预估领域提出了一系列相关工作 被称为特
  • 总有人说鸿蒙是安卓套壳?鸿蒙5.0之后彻底摆脱安卓

    鸿蒙系统的操作逻辑与安卓基本上差不多 这和安卓系统没啥区别 是不是就是安卓系统套了一个壳啊 为什么到今天还是有不少人在争论它到底是不是安卓套壳 这与鸿蒙早期 完全自主研发 的 过激 宣传不无关系 其次就是鸿蒙生态环境上的不足 确实 华为一开
  • 鸿蒙系统的崛起对程序员来说是机遇、还是挑战呢?

    前言 最近 一个话题在程序员圈子里激起了热烈讨论 那就是鸿蒙系统的崛起是否会影响程序员的就业和发展 我该转去学鸿蒙开发吗 鸿蒙前景如何 值不值得投入时间研究 对此 程序员们表达了各种疑虑和困惑 的确 一个全新的操作系统的出现确实让人眼花缭乱
  • go-zero 开发之安装 etcd

    本文只涉及 Linux 上的安装 二进制安装 下载二进制安装包 ETCD VER v3 4 28 ETCD VER v3 5 10 DOWNLOAD URL https github com etcd io etcd releases do
  • Quartz定时任务运行时,能够否对某个任务重新调度呢?

    背景 quartz 是一个功能丰富 开源 分布式的任务调用框架 我参与的很多项目都用它来实现定时调度功能 关于定时任务 有一个常见的需求是 由 Web 应用来控制定时任务的启动 停止 调度周期等 本文探讨的是 对于当前正在 调度的 耗时较长
  • go-zero开发入门之网关往rpc服务传递数据2

    go zero 的网关服务实际是个 go zero 的 API 服务 也就是一个 http 服务 或者说 rest 服务 http 转 grpc 使用了开源的 grpcurl 库 当网关需要往 rpc 服务传递额外的数据 比如鉴权数据的时候
  • 一呼百应!腾讯、阿里等全都支持鸿蒙了,安卓该担心了

    前言 众所周知 目前华为鸿蒙系统 已经是全球第三大智能手机系统 仅次于安卓 iOS 不过大家也都清楚 这个第三 实际上还是有水份的 因为鸿蒙其实并没有自己的生态 靠的是兼容安卓生态 真正的纯血鸿蒙APP 仅几十个 如果靠着这几十个APP 完
  • 短视频制作:从构思到发布的全方位指南

    在当今数字化时代 短视频已成为备受欢迎的媒体形式 凭借其简洁有趣的内容 短视频成功吸引了大量观众的关注 然而 制作一部引人入胜的短视频并非易事 本文将为你提供从目标设定到平台发布的全面指导 帮助你制作出令人难以忘怀的短视频 第一步 明确目标
  • 有哪些PDF转图片工具好用?PDF转图片免费软件推荐

    在一个阳光明媚的下午 你正在翻阅着一份重要的PDF文件 想要快速将其中的内容以图片形式分享给朋友 然而 复制粘贴不仅繁琐 还会失去原本的排版和格式 那么 如何将PDF文件转换成图片呢 今天就来介绍两款可以实现这一功能的免费软件 如果你也想知
  • 你知道ai写作工具哪个好吗?教你用AI写年终总结

    又是一年的十二月到了 每年到这个时候 朋友圈都总会出现一首常驻歌曲 十二月的奇迹 身为打工人的大家应该都希望 在忙碌了一年的最后一个月被奇迹眷顾吧 不过俗话说得好 靠人不如靠己 与其把自己交给命运的奇迹 那不如自己也努力争取一下 在老板面前
  • 鸿蒙开发入门:快速修复命令行调试开发指导

    快速修复命令行调试开发指导 当前阶段 HarmonyOS为开发者提供了命令行的调试开发工具可供使用 比如 包名为com ohos quickfix的示例应用 版本号为1000000 该应用的当前版本运行中有某问题需要修复 此时 开发者可参考
  • 主动学习与弱监督学习

    人工智能数据的获取没有想象中的那么简单 虽然我们早已身处大数据的浪潮下 很多公司在获取数据的大浪中翻滚却始终没有找到一个合适的获取数据的渠道 很多情况下 获取高质量的人工智能数据需要消耗大量的人力 时间 金钱 但是对于未来世界 以 人机协同
  • Java处理SSH-免密登录

    前提 需要测试主机之间能够免密 配置ssh请自行百度 jar包 旧版 com jcraft jsch 仅支持老版的密钥格式 旧版本 RSA
  • go-zero开发入门-API网关开发示例

    开发一个 API 网关 代理 https blog csdn net Aquester article details 134856271 中的 RPC 服务 网关完整源代码 file main go package main import
  • 设计之妙,理解Android动画流程

    本文基于Android 12进行学习研究 参考 深入理解Android内核源码 思路学习总结 如有理解不对 望各位朋友指正 另外可能留存一些疑问 留后续再探索 输出只是为了分享和提升自己 动画初始化 按照窗口管理策略类中的定义 动画应该被分
  • 创建个人网站(一)从零开始配置环境,搭建项目

    目录 前言 配置环境 前端 后端 遇到的问题 1 安装了nvm和node vscode没反应 2 安装完脚手架之后vue指令不存在
  • docker配置连接harbor私有仓库

    一 前言 以下分为两种情况说明docker对harbor私有仓库的访问配置 一种是harbor使用自建证书配置https 一种是使用公有证书配置https 二 docker配置 harbor使用自建证书的情况 使用自建证书对harbor进行
  • 不看后悔系列!Android面试经验分享,附经典题库+答案解析

    前言 近期 许多同学向我咨询关于Android技术岗位的招聘事宜 希望能够在求职过程中更好地准备 以冲击大厂 拿到高薪 作为首批Android开发者 我十余年来一直深耕Android及移动互联网开发领域 拥有丰富的面试和实战经验 在此 我想
  • 活动报名|马普脑研究所主任Moritz Helmstaedter:Connectomics连接组学

    报告主题 Connectomics连接组学 报告日期 12月08日 周五 15 30 16 30 主题简介 大脑是由数百万至数十亿神经元组成的高度互联的网络 一个世纪以来 我们一直无法在突触分辨率上绘制这些连通性网络的图谱 只是最近 利用新
  • 协程与互斥锁: Kotlin Mutex的终极指南

    引言 今天我们将深入研究Kotlin中的Mutex 互斥锁 原理以及在实际开发中的使用技巧 Mutex是多线程编程中的关键工具 它可以有效地解决多线程访问共享资源时可能发生的竞态条件问题 Mutex的基本原理 Mutex是互斥锁的缩写 它是