Kotlin协程核心理解

一、协程是什么?

1.1 基本概念的理解

    我们知道JVM中的线程的实现是依赖其运行的操作系统决定的,JVM只是在上层进行了API的封装,包含常见的有线程的启动方法,状态的管理,比如:Java中抽象出了6种状态,提供了start方法用于启动线程。
    但是线程一旦调用start()开始执行,那我们是很难再控制线程的停止的,尽管jdk中提供了suspend()方法,但是suspend也只是做了标记线程需要中断,最终是否中断,什么时候中断还是依赖操作系统的具体实现逻辑,从语言层面来说是无法直接控制的。

// java线程的状态定义在Java$State枚举对象中
public enum State {/*** Thread state for a thread which has not yet started.*/NEW,/*** Thread state for a runnable thread.  A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>*   <li>{@link Object#wait() Object.wait} with no timeout</li>*   <li>{@link #join() Thread.join} with no timeout</li>*   <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called {@code Object.wait()}* on an object is waiting for another thread to call* {@code Object.notify()} or {@code Object.notifyAll()} on* that object. A thread that has called {@code Thread.join()}* is waiting for a specified thread to terminate.*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>*   <li>{@link #sleep Thread.sleep}</li>*   <li>{@link Object#wait(long) Object.wait} with timeout</li>*   <li>{@link #join(long) Thread.join} with timeout</li>*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/TERMINATED;}

    而协程内的代码依然执行在线程上,因为线程是CPU调度的基本单元这个大前提还是不变的,属于操作系统层面的基本概念了。但是协程通过使用状态机的方式在语言层面上实现了一种状态、生命周期更易管控的代码逻辑调度框架(语言层面的框架),也可以理解为轻量级线程,并且不像线程那样直接使用操作系统实现的线程,一旦启动基本就只能等任务执行结束或请求中断待能中断时才停止运行。

1.2 协程和线程、进程的关系

进程、线程和协程的关系示意图

启动一个线程执行任务:

val task1 = Thread {val result = requestUserInfo()println("task1 finished, result = $result")
}
task1.start()

启动一个协程执行任务:

val task1 = launch {val result = requestUserInfo()println("task1 finished, result = $result")
}
// requestUserInfo()需要切换协程运行的线程需要增加suspend修饰,
// 定义成挂起函数
suspend fun requestUserInfo(): UserInfo = withContext(Dispatchers.IO) {delay(500)return@withContext UserInfo("10000", "zhangsan")
}

总结一下,协程和线程的区别:

  • 线程一旦开始执行就不会暂停,直到任务结束,这个过程是连续的
  • 协程能够自己挂起和恢复,语言层面实现了挂起和恢复流程,能够实现协作式调度
1.3 使用协程的关键API
1.3.1 协程作用域:CoroutineScope

    创建协程或调用挂起函数必须有协程作用域,kotlin创建作用域有三种办法,GlobalScope、runBlocking和CoroutineScope()方法。
三种协程作用域的用法

  • Android中提供的协程作用域有:
    • MainScope()
      MainScope()

    • lifecycleScope
      在这里插入图片描述
      lifecycleScope中的协程会在Activity销毁时执行cancel

    • viewModelScope
      viewModelScope

1.3.2 协程对象:Job
public interface Job : CoroutineContext.Element {// 注(1)public companion object Key : CoroutineContext.Key<Job>// 如果协程还未启动,比如传入的start对象是LAZY,可通过主动调用// start方法启动协程public fun start(): Boolean// 注(2)public fun cancel(cause: CancellationException? = null)// 当前协程的子协程public val children: Sequence<Job>// 附加子协程,使当前协程对象成为父协程@InternalCoroutinesApipublic fun attachChild(child: ChildJob): ChildHandle// 等待当前协程执行完成,比如调用协程的cancel()方法后,调用join()// 就是等待协程cancel执行完成public suspend fun join()// 注册在取消或完成此作业时 同步 调用一次的处理程序public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle@InternalCoroutinesApipublic fun invokeOnCompletion(onCancelling: Boolean = false,invokeImmediately: Boolean = true,handler: CompletionHandler): DisposableHandle
}
  • (1)Key : 声明成伴生对象后,只要是同一种类型的Job创建出来的不同Job实例,key都是相同的,也就是同类型的Job对象key也相同
  • (2)cancel(): 取消协程
1.3.3 协程上下文:CoroutineContext

    存放协程相关的一些信息

1.3.4 协程调度器:CoroutineDispatcher
  • Dispatchers.Main: Android中特有的,在主线程中执行协程代码,其他平台使用会抛出异常
  • Dispatchers.IO: 用于IO密集型的协程任务
  • Dispatchers.Default: 用于CPU密集型的协程任务
  • Dispathcers.Unconfined: 不指定协程执行的线程调度器

二、协程在Android中的常见用法

2.1 子线程中执行耗时任务后切到主线程更新UI
// 场景1: 在子线程中执行耗时任务后,切到主线程处理
coroutineScope.launch {// 挂起函数,执行时从当前线程中脱离,执行在dispatcher执行的线程中,执行完毕后再切换原来的线程中// 挂起后当前协程下一行代码会等待挂起函数执行完成val result = withContext(Dispatchers.IO) {// 在Dispatchers.IO(线程调度器)指定的子线程中执行下面的代码delay(5000)100}Log.d(TAG, "onCreate: main 2 =========> $coroutineContext")binding.tvNews.text = result.toString()
}

上面的用法对于Android来说,协程是一个异步代码执行框架,相比于Thread+Handler的方式更加简洁,省去了开发者编写线程切换代码的工作。

2.2 多个耗时任务并行执行合并结果【常见的业务模型】

    在Android业务中我们经常需要并行开始多个业务接口请求,然后合并成一个结果,进行后续业务逻辑的判断、UI的展示,使用Jdk提供的CountDownLatch,RxJava的zip都可以实现类似的功能逻辑。
如下展示了kotlin在这个业务模型中如何实现:

coroutineScope.launch {// 在Dispatchers.IO执行的线程中执行任务1val async1Result = async(Dispatchers.IO) {Log.d(TAG, "onCreate: async1 $coroutineContext")executeTask1()}// 在Dispatchers.IO执行的线程中执行任务2val async2Result = async(Dispatchers.IO) {Log.d(TAG, "onCreate: async2 $coroutineContext")executeTask2()}// 在调用async方法之后两个协程任务都已经并行跑起来了,这时候调用await方法等待执行结果val result = async1Result.await() + async2Result.await()Log.d(TAG, "onCreate: async result = $result")
}
  • kotlin中使用async实现类似java中Callable的协程任务,但是await方法阻塞等待结果并没有提供超时时间的参数
  • async()方法是创建一个可获取返回值的协程对象,类型是Deferred,继承自Job

三、挂起函数的理解

3.1 挂起函数的本质
  • 协程的核心是函数或一段程序能够支持挂起,执行完成后又从挂起位置恢复,然后继续执行后面的代码。
  • kotlin的是借助线程实现的,是对线程的一个封装框架,通过launch、async启动一个协程,其实就是启动一个闭包中的代码块。
  • 当执行到suspend函数时,暂时不执行协程代码了,而是从当前线程中脱离,函数内的逻辑转到协程调度器所指定的线程中去执行,等到挂起函数执行完毕后,又恢复都挂起的位置,继续执行后续逻辑

所以总结来说,挂起函数就是切到别的线程,稍后又能够自动切回来的线程调度操作。

3.2 为什么挂起函数一定要在协程或挂起函数中调用?

    挂起函数切到调度器线程中后,是需要协程框架主动调用resumeWith方法再切回来的,如果在非协程非挂起函数调用,那么就没有协程环境,无法切回来,就无法实现挂起的执行逻辑。

四、协程的取消

协程提供了cancel方法进行取消。

class Job {public fun cancel(cause: CancellationException? = null)
}

cancel()方法其实还有另外两个重载方法,但是打上了@Deprecated注解,所以不再使用了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/173247.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法之矩阵: Leetcode 134. 螺旋矩阵 (Typescript版)

螺旋矩阵 https://leetcode.cn/problems/spiral-matrix/ 描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示…

人工智能(AI)在医疗领域的应用

人工智能&#xff08;AI&#xff09;在医疗领域的应用 人工智能&#xff08;AI&#xff09;在医疗领域的应用近年来得到了广泛的关注。其中&#xff0c;AI辅助治疗疾病的技术成为了研究热点。本文将介绍AI辅助治疗疾病的技术&#xff0c;包括其定义、应用场景、案例分析和发展…

3.线性神经网络

#pic_center R 1 R_1 R1​ R 2 R^2 R2 目录 知识框架No.1 线性回归基础优化算法一、线性回归1、买房案例2、买房模型简化3、线性模型4、神经网络5、损失函数6、训练数据7、参数学习8、显示解9、总结 二、 基础优化算法1、梯度下降2、学习率3、小批量随机梯度下降4、批量大小5、…

1024 云上见 · 上云挑战(ChatGPT搭建)

【玩转1024】使用函数计算X通义千问搭建AI助手&#xff0c;参与1024小说创作大赛 【使用函数计算X通义千问搭建AI助手&#xff0c;参与小说创作大赛】&#xff1a;本活动基于函数计算X 通义千问快速部署 AI 个人助手应用&#xff0c;用户可以根据需要选择不同角色的AI助手开启…

基于引力搜索算法的无人机航迹规划-附代码

基于引力搜索算法的无人机航迹规划 文章目录 基于引力搜索算法的无人机航迹规划1.引力搜索搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用引力搜索算法来优化无人机航迹规划。 …

Android12之#pragma clang diagnostic ignored总结(一百六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【网络安全 --- 文件上传靶场练习】文件上传靶场安装以及1-5关闯关思路及技巧,源码分析

一&#xff0c;前期准备环境和工具 1&#xff0c;vmware 16.0安装 若已安装&#xff0c;请忽略 【网络安全 --- 工具安装】VMware 16.0 详细安装过程&#xff08;提供资源&#xff09;-CSDN博客文章浏览阅读186次&#xff0c;点赞9次&#xff0c;收藏2次。【网络安全 --- 工…

生活日用品经营小程序商城的作用是什么

生活日用品如牙膏牙刷、护手霜等小产品是人们生活所需&#xff0c;传统客户会通过线下超市商场购买&#xff0c;但现在的消费者往往会选择线上渠道购买&#xff0c;省时省力坐在家里等货上门即可。 因此对商家来说&#xff0c;需要打通线上渠道才能获得更多生意&#xff0c;提…

HCIA数据通信——交换机(Vlan间的通信与安全)

前言 之前的提到了交换机的概念和实验。不过交换机的一些功能还没有说完&#xff0c;我们的实验也仅仅是阻止相同地址段的IP地址互通&#xff0c;也没有用到子接口和路由器。显然&#xff0c;那样的配置过于简单。 端口安全 Port Security&#xff08;端口安全&#xff09;的功…

ActiveMQ

ActiveMQ 安装 下载网址&#xff1a;ActiveMQ 一定要和自己安装的jdk版本匹配&#xff0c;不然会报错 下载到本地之后解压缩 有可能端口号被占用 解除端口号占用&#xff0c;参考&#xff1a;Windows_端口被占用 打开cmd 查询所有的端口号 netstat -nao查询指定端口号 n…

2023-10-21 美团2024秋招后端开发岗笔试题

1 考察dfs和拓扑排序 1.1 题目描述&#xff08;如果拓扑排序不清楚可以去做一下lc 207. 课程表&#xff09; 1.2 答案 import java.util.*;public class Meituan {static int m,n;public static void main(String[] args) {Scanner in new Scanner(System.in);m in.nextInt…

基于入侵杂草算法的无人机航迹规划-附代码

基于入侵杂草算法的无人机航迹规划 文章目录 基于入侵杂草算法的无人机航迹规划1.入侵杂草搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用入侵杂草算法来优化无人机航迹规划。 …

记录几个常用的docker镜像

背景 Docker 部署有着非常多的优势&#xff0c;可以帮助提高开发、测试和部署的效率&#xff0c;降低成本&#xff0c;使应用更具可移植性和可扩展性&#xff0c;包括但不限于 标准化应用发布&#xff0c;跨平台和主机使用&#xff1a;Docker的镜像提供了标准化发布环境&…

三篇论文:速览GPT在网络安全最新论文中的应用案例

GPT在网络安全领域的应用案例 写在最前面论文1&#xff1a;Chatgpt/CodeX引入会话式 APR 范例利用验证反馈LLM 的长期上下文窗口&#xff1a;更智能的反馈机制、更有效的信息合并策略、更复杂的模型结构、鼓励生成多样性和GPT类似的步骤&#xff1a;Conversational APR 对话式A…

Visual Studio Code的下载与安装

Visual Studio Code&#xff08;简称 VS Code&#xff09;是由 Microsoft 开发的免费、开源的文本编辑器&#xff0c;适用于多种操作系统&#xff0c;包括 Windows、macOS 和 Linux。它的设计目标是成为一款轻量级、高效的代码编辑工具&#xff0c;同时提供丰富的扩展和功能&am…

mysql查看数据表文件的存放路径

mysql查看数据表文件的存放路径_怎么看mysql表的位置在哪-CSDN博客 问题&#xff1a; 我们在mysql的安装目录中没有找到data&#xff08;数据库存放的地方&#xff09;的文件夹&#xff0c;我们需要找到数据库文件data的存放目录。 解决方法&#xff1a;在mysql的cmd中输入以下…

windows8080端口占用

查看端口占用 netstat -ano | findstr “8080”查看占用进程 tasklist | findstr “4664”关闭占用进程 taskkill /f /t /im httpd.exe

【Jenkins】新建任务FAQ

问题1. 源码管理处填入Repository URL&#xff0c;报错&#xff1a;无法连接仓库&#xff1a;Error performing git command: ls-remote -h https://github.com/txy2023/GolangLearning.git HEAD 原因&#xff1a; jenkins全局工具配置里默认没有添加git的路径&#xff0c;如果…

VBA技术资料MF75:测量所选单元格范围的高度和宽度

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

基于水循环算法的无人机航迹规划-附代码

基于水循环算法的无人机航迹规划 文章目录 基于水循环算法的无人机航迹规划1.水循环搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用水循环算法来优化无人机航迹规划。 1.水循环…