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注解,所以不再使用了。

五、协程并发数据同步问题

    在java中多线程访问共享变量时,因为有主内存和工作内存模型的设计。

class Test {private var count = 0private val mutex = Mutex()suspend fun test() = withContext(Dispatchers.IO) {repeat(100) {launch {repeat(1000) {count++}}}launch {delay(3000)println("finished----> count = $count")}}
}fun main(): Unit = runBlocking {runBlocking {Test().test()}
}

输出结果:

finished----> count = 97861

如上代码,在循环中创建100个协程,每个协程中对count变量累加1000次,最终count的值应该是100000,但是输出是97861,存在并发修改数据不一致问题。
在Java线程中,我们可以采用sychronized或ReentrantLock方式进程多线程同步处理,保证最终数据的正确性。

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

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

相关文章

深入理解指针3

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 字符指针变量 2. 数组指针变量 2.1 数组指针变量是什么&#xff1f; 2.2 数组指针变量怎么初始化 3. 二维数组传参的本质 4. 函数指针变量 4.1 函数指针变量的创…

并发安全问题之超卖问题

并发安全问题之超卖问题 乐观锁总结&#xff1a; 优点&#xff1a;不加锁性能好。 缺点&#xff1a;同时请求成功率低&#xff08;即只要发现数据变了就放弃了&#xff09;。 乐观锁思想的具体体现&#xff1a;一共两步&#xff0c;第一步&#xff0c;先查询状态。第二步&…

【数据库】

文章目录 1. 聚合函数练习&#xff1a; 2. 子查询 1. 聚合函数 where中过滤条件中不能写聚合函数&#xff0c;有聚合函数需要写到Having中 方式一效率高&#xff1a; Select执行流程 练习&#xff1a; 2. 第七题&#xff1a;count(*)有问题&#xff0c;原因是左外连接后…

Apache Doris (四十八): Doris表结构变更-替换表

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录

Leetcode—275.H指数II【中等】

2023每日刷题&#xff08;十三&#xff09; Leetcode—275.H指数II 算法思想 实现代码 int minValue(int a, int b) {return a < b ? a : b; }int hIndex(int* citations, int citationsSize){int left, right;left 0;right citationsSize - 1;while(left < right) …

Openssl数据安全传输平台017:Linux客户端代码的编译与调试-Bug记录

文章目录 1 在windows上先预编译2 Centos上进入项目文件夹进行编译2.0 最终的编译指令2.1 找不到lprotobuf&#xff0c;找不到protobuf的google文件夹2.1.1 编译指令及提示2.1.2 问题分析2.1.3 解决办法 2.2 json类中方法unreference2.2.1 编译指令及提示2.2.2 问题分析 *** 最…

【工具使用】NPS内网穿透工具介绍

文章目录 前言一、内网穿透二、NPS概述三、NPS原理四、NPS服务器搭建(一)云服务器配置 五、NPS内网穿透演示(一)演示案例一(二)演示案例二 六、NPS内网穿透检测建议(一)流量监控(二)流量协议分析(三)网络行为异常检测 七、NPS内网穿透防范建议(一)阻止或隔离流量(二)更新和强化…

[架构之路-245/创业之路-76]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 企业资源管理计划ERP

目录 前言&#xff1a; 一、企业信息化的结果&#xff1a;常见企业信息化软件 1.1 企业资源管理计划 1.1.1 什么是ERP&#xff1a;企业最常用的信息管理系统 1.1.2 ERP的演进过程 1.1.3 EPR模块 1.1.4 EPR五个层级 1.1.5 企业EPR业务总体流程图 1.1.6 什么类型的企业需…

常用 sqlite3 命令

本次将向您讲解 SQLite 编程人员所使用的简单却有用的命令。这些命令被称为 SQLite 的点命令&#xff0c;这些命令的不同之处在于它们不以分号 ; 结束。 让我们在命令提示符下键入一个简单的 sqlite3 命令&#xff0c;在 SQLite 命令提示符下&#xff0c;您可以使 用各种 …

【MyBatis Plus】初识 MyBatis Plus,在 Spring Boot 项目中集成 MyBatis Plus,理解常用注解以及常见配置

文章目录 一、初识 MyBatis Plus1.1 MyBatis Plus 是什么1.2 MyBatis Plus 和 MyBatis 的区别 二、在 Spring Boot 项目中集成 MyBatis Plus2.1 环境准备2.2 引入 MyBatis Plus 依赖2.3 定义 Mapper2.4 测试 MyBatis Plus 的使用 三、MyBatis Plus 常用注解3.1 为什么需要注解3…

【C++初探:简单易懂的入门指南】二

【C初探&#xff1a;简单易懂的入门指南】二 1.引用1.1引用做函数的参数1.2 引用做返回值1.2.1 关于引用做返回值的几点补充 1.3 多引用(对一个变量取多个别名)1.4 引用类型一致性原则以及权限的问题阐述1.5引用的效率问题1.6引用和指针的比较 2.auto关键字2.1 auto关键字的使用…

共谈信创谋发展 | 开源网安主办的信创生态构建沙龙圆满完成

​10月26日&#xff0c;由珠海市工业和信息化局、珠海市高新区科技创新和产业发展局指导&#xff0c;珠海华发产业园与开源网安珠海公司等联合主办的“赋能数字转型 提速国产替代”—Uni-Idea信创生态构建沙龙在华发信创产业园成功举办&#xff0c;近百位行业代表参加本次活动&…

使用requests库进行HTTP爬虫编程

目录 一、安装requests库 二、发送HTTP请求 三、解析HTML页面 四、处理HTTP响应和异常 五、使用代理和会话管理 六、使用多线程或多进程提高效率 七、数据存储和处理 八、注意事项和总结 在当今的数字化世界中&#xff0c;数据已经成为了一种宝贵的资源。而网络爬虫程序…

基本微信小程序的体检预约小程序

项目介绍 我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;体检预约系统小程序被用户普遍使用&#xff0c;为方便用户…

使用ControlNet生成视频(Pose2Pose)

目录 ControlNet 介绍 ControlNet 14种模型分别是用来做什么的 ControlNet 运行环境搭建 用到的相关模型地址 ControlNet 介绍 ControlNet 是一种用于控制扩散模型的神经网络结构&#xff0c;可以通过添加额外的条件来实现对图像生成的控制。它通过将神经网络块的权重复制到…

OPNET <<< Program Abort >>> Standard function stack imbalance

OPNET <<< Program Abort >>> Standard function stack imbalance OPNET 问题原因及解决办法 OPNET 问题 OPNET仿真时遇到此问题&#xff1a; <<< Program Abort >>> Standard function stack imbalance 原因及解决办法 出现此问题是因…

【设计模式】第14节:结构型模式之“代理模式”

一、简介 代理模式&#xff08;Proxy Design Pattern&#xff09;在不改变原始类&#xff08;或叫被代理类&#xff09;代码的情况下&#xff0c;通过引入代理类来给原始类附加功能。 二、优点 关注点分离访问控制延迟实例化远程访问缓存增加附加功能 三、应用场景 访问控…

【2021集创赛】海云捷迅杯一等奖:基于稀疏卷积与层融合的流水线优化方案

海云捷迅杯:基于FPGA C5Soc的MobileNetV1 SSD目标检测方案设计 本作品参与极术社区组织的有奖征集|秀出你的集创赛作品风采,免费电子产品等你拿~活动。 **杯赛题目&#xff1a;**海云捷迅杯——基于FPGA C5Soc的MobileNetV1 SSD目标检测方案设计 设计任务&#xff1a; 基于已训…

STM32G030F6P6 芯片实验 (一)

STM32G030F6P6 芯片实验 (一) 淘宝搞了几片, 没试过 G系列, 试试感觉. 先搞片小系统版: 套 STM32F103C8T6小系统板格式. 原理图: (1) Ref 有点跳, 从 STM32F103C8T6 系统板改的, 没重编号. (2) Type-C 纯给电, 砍了 16pin的, 直接换 6pin的。 (3) 测试LED放 B2。 (4) 测试底…

Android 10.0 framework关于systemUI状态栏透明背景的功能实现

1.概述 在10.0的系统产品定制化开发中,在对于系统原生SystemUI的状态栏背景在沉浸式状态栏的 情况下默认是会随着背景颜色的变化而改变的,在一些特定背景下状态栏的背景也是会改变的,所以由于产品开发需要 要求需要设置状态栏背景为透明的,所以就需要在Activity创建的时候…