Kotlin协程核心理解

news2024/11/26 0:47:41

一、协程是什么?

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>
	// 附加子协程,使当前协程对象成为父协程
	@InternalCoroutinesApi
    public fun attachChild(child: ChildJob): ChildHandle
    // 等待当前协程执行完成,比如调用协程的cancel()方法后,调用join()
    // 就是等待协程cancel执行完成
    public suspend fun join()
    // 注册在取消或完成此作业时 同步 调用一次的处理程序
    public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle
    @InternalCoroutinesApi
    public 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执行的线程中执行任务1
    val async1Result = async(Dispatchers.IO) {
        Log.d(TAG, "onCreate: async1 $coroutineContext")
        executeTask1()
    }
    // 在Dispatchers.IO执行的线程中执行任务2
    val 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.coloradmin.cn/o/1147080.html

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

相关文章

软件测试自学路线

一、简介 本文从一个简单的登录接口测试入手&#xff0c;一步步调整优化接口调用姿势&#xff0c;然后简单讨论了一下接口测试框架的要点&#xff0c;最后介绍了一下我们目前正在使用的接口测试框架pithy。期望读者可以通过本文对接口自动化测试有一个大致的了解。 二、引言 …

0038Java程序设计-基于java高校社团管理系统论

文章目录 **摘 要****目** **录**系统实现开发环境 摘 要 当前&#xff0c;大多数高校的社团信息管理都是采用纸质档案的方式来管理的&#xff0c;这样不仅不能长期的保存信息&#xff0c;而且在数据的查找上带来很大的不方便。在目前的网络技术和计算机技术的普及和信息管理的…

AVL树、红黑树的介绍和实现[C++]

本文主要对AVL树和红黑树的结构和实现方法进行一定的介绍&#xff0c;仅实现部分接口。 目录 一、AVL树 1.AVL树的概念 2.AVL树节点的定义 3.AVL树的插入 4.AVL树的旋转 1. 新节点插入较高左子树的左侧——左左&#xff1a;右单旋 2. 新节点插入较高右子树的右侧——右…

MySQL(1):开始

概述 DB&#xff1a;数据库&#xff08;Database&#xff09; 即存储数据的“仓库”&#xff0c;其本质是一个文件系统。它保存了一系列有组织的数据。 DBMS&#xff1a;数据库管理系统&#xff08;Database Management System&#xff09; 是一种操纵和管理数据库的大型软件…

对Linux线程的理解(什么是线程,线程的创建终止等待分离,线程互斥,Linux常见的锁,线程同步),两万字总结,有这一篇文章就够了!

文章目录 一、什么是线程1.线程是怎样描述的2.线程与进程的区别3.线程的优缺点4.理解Linux的轻量级进程 二、Linux线程控制1.线程创建:pthread_create()2.线程终止:pthread_exit()3.线程等待:pthread_join()4.分离线程:pthread_detach() 三、Linux线程互斥1.互斥量2.线程安全与…

UNUNX安全的交易所

去中心化 传统意义上的交易所都属于中心化的交易所&#xff0c;用户需要在交易所注册人个信息&#xff0c;办理银行卡&#xff0c;充值到交易所帐号才能交易此时你的资产是在交易所被交易所托管&#xff0c;如果交易所做恶用户将会蒙受损失&#xff0c;交易所关闭跑路的案例也…

私有云:【5】安装VCenter Server

私有云&#xff1a;【5】安装VCenter Server 1、在本地物理机上安装VCenter Server到Esxi1.1、开始安装第一阶段1.2、开始安装第二阶段 2、配置VCenter2.1、分配许可2.2、添加主机2.3、创建数据存储NFS 1、在本地物理机上安装VCenter Server到Esxi 安装前在AD域服务器配置好VC…

jmeter BeanShell预处理程序:报错JSONObject not found in namespace

1、jmeter运行报错: ERROR o.a.j.u.BeanShellInterpreter: Error invoking bsh method: eval Sourced file: inline evaluation of: ". . . : Typed variable declaration : Class: JSONObject not found in namespace WARN o.a.j.m.BeanShellPreProcessor: Problem…

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matla…

企业年度经营计划预算最全攻略来了!附年度费用预算表格模板

经营分析里最重要&#xff0c;也最让人头大的部分&#xff0c;莫过于制定经营计划。现实中很多企业因为制定年度经营预算费用不善&#xff0c;导致年度经营计划与预算脱节&#xff0c;年度计划成了摆设…… 年度费用预算表 为了摆脱企业年度经营与发展&#xff0c;并作出科学合…

[云原生案例1.] 构建LNMP架构并运行Wordpress个人博客平台

文章目录 1. 当前需求2. 前置准备3. 搭建过程3.1 创建自定义网络3.2 部署并配置nginx3.2.1 创建工作目录并上传相关软件包3.2.2 解压缩相关软件包3.2.3 编写Dockerfile文件3.2.4 编写nginx.conf文件3.2.5 创建nginx镜像3.2.6 运行容器 3.3 部署并配置mysql3.3.1 创建工作目录3.…

JavaWeb-jdbc的mysql驱动问题

jdbc的mysql驱动问题 问题描述&#xff1a;mysql驱动在服务器运行时显示找不到类&#xff0c;在程序运行时正常。 解决办法&#xff1a;主要是驱动没有放对目录&#xff0c;将mysql驱动放到WEB-INF/lib目录下就能正常访问了 添加为库 第2种办法&#xff0c;pom文件里添加mys…

lwip代码分析

lwIP&#xff08;Lightweight IP&#xff09;是一个为嵌入式系统设计的轻量级TCP/IP协议栈。它旨在为资源受限的环境提供完整的网络协议功能&#xff0c;同时保持低内存使用和代码大小。由于其模块化的设计&#xff0c;开发者可以根据需要选择包含或排除特定功能&#xff0c;以…

加解密原理(HCIA)

一、加密技术 1、加密的两个核心组件 2、加密技术作用&#xff1a; 二、加解密技术原理 1、对称加密 2、非对称加密 &#xff08;1&#xff09;思考问题&#xff1f; 1&#xff09;、有了非对称加密为什么还用对称加密&#xff1f; 2&#xff09;、如何传递秘钥呢&…

【微信小程序开发】学习小程序的网络请求和数据处理

前言 网络请求是微信小程序中获取数据和与服务器交互的重要方式。微信小程序提供了自己的API来处理网络请求&#xff0c;使得开发者可以轻松地在微信小程序中实现数据的获取和提交。本文将介绍微信小程序中的网络请求&#xff0c;包括使用wx.request发起GET和POST请求&#xf…

基于RK3568高性价比全国产EMS储能解决方案(二)设计方案

目录 版 本 修 订 记 录 1. 产品介绍 1.1. 什么是XM3568-EP 1.2. 产品特点 1.3. 外壳尺寸 1.4. 外壳外观 1.5. 规格参数 2. 设备使用介绍 2.1. 下载需要使用到的驱动和调试工具 2.2. 启动网关 2.3. DEBUG串口的使用方法 2.4. LED指示灯说明 3. Linux系…

【每日一题】274. H 指数-2023.10.29

题目&#xff1a; 274. H 指数 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他…

计算机视觉注意力机制小盘一波 (学习笔记)

将注意力的阶段大改分成了4个阶段 1.将深度神经网络与注意力机制相结合&#xff0c;代表性方法为RAM ⒉.明确预测判别性输入特征&#xff0c;代表性方法为STN 3.隐性且自适应地预测潜在的关键特征&#xff0c;代表方法为SENet 4.自注意力机制 通道注意力 在深度神经网络中…

ARPG----C++学习记录01日志和调试

多人射击有点难&#xff0c;发现这个更加基础&#xff0c;先学习这个 显示日志 可以在代码中插入这样一行来打印日志&#xff0c;蓝图里的printstring会在屏幕和日志里都显示。可以使用%f&#xff0c;d等来获取后边的输入值。对于打映字符串变量&#xff0c;传入需要* UE_LOG…

基于 matplotlib 实现的基本排序算法的动态可视化项目源码,通过 pyaudio 增加音效,冒泡、选择、插入、快速等排序

基本排序算法动态可视化 依托 matplotlib 实现的基本排序算法的动态可视化&#xff0c;并通过 pyaudio 增加音效。 安装 在使用之前请先检查本地是否存在以下库&#xff1a; matplotlibpyaudiofire requirements.txt 中包含了上述的库 使用 目前本项目仅提供了以下排序算…