1、什么是协程?和线程有什么区别
答案
协程是轻量级线程 ,基于线程封装,由语言层调度,不是操作系统调度。
区别:
- 线程由 OS 调度、开销大;协程开销极小、可开上万个。
- 线程是抢占式 ;协程是协作式,主动挂起、恢复。
- 协程可非阻塞挂起,不阻塞当前线程。
- 协程自带上下文、作用域、生命周期,更适合 Android 异步开发。
2、协程的优点
答案
- 轻量、占用资源少,支持大量并发;
- 用同步写法写异步代码,告别嵌套回调;
- 自带作用域,可统一取消、防泄漏;
- 灵活切换调度线程(主线程 / 子线程);
- 配合 Flow、Jetpack 生态适配更好。
3、什么是挂起函数 suspend?作用是什么
答案
suspend 修饰的函数是挂起函数;
特点:
- 只能在协程或其他挂起函数中调用;
- 不会阻塞线程,只是挂起当前协程,让出线程执行其他任务;
- 耗时结束后自动恢复继续执行。
4、suspend 底层原理
答案
Kotlin 编译器把挂起函数编译成 状态机,把代码切分成多个状态片段;
挂起点保存上下文,恢复时从对应状态继续执行,实现无回调异步。
5、协程三大核心:协程作用域、调度器、生命周期
答案
- 作用域 Scope:管理协程生命周期,统一取消、控制启停;
- 调度器 Dispatcher:指定协程跑在哪个线程;
- Job:协程任务句柄,可取消、监听完成状态。
6、四大调度器 Dispatcher 区别
答案
- Dispatcher.Main:主线程,更新 UI;
- Dispatcher.IO:子线程,适合网络、文件、数据库 IO;
- Dispatcher.Default :子线程,适合CPU 密集型复杂计算;
- Dispatcher.Unconfined:不指定线程,随上下文执行,几乎不用。
7、常用协程作用域有哪些
答案
- GlobalScope :全局作用域,生命周期跟 App 一致,不推荐,容易泄漏;
- viewModelScope :ViewModel 自带,ViewModel 销毁自动取消协程,防泄漏;
- lifecycleScope:Activity/Fragment 自带,页面销毁自动取消;
- 自定义 CoroutineScope 自己管理生命周期。
8、为什么不推荐用 GlobalScope
答案
GlobalScope 生命周期贯穿整个 App,页面退出无法自动取消;里面的网络、延时任务还在后台跑,极易造成内存泄漏、浪费资源。
9、launch 和 async 区别
答案
- launch :只执行任务,无返回值,返回 Job;
- async :有返回值,返回 Deferred ,可用
await()拿结果;适合需要获取异步返回值的场景。
10、await () 作用
答案
等待 async 协程执行完成,挂起当前协程,拿到返回结果;不会阻塞线程,只是协程挂起。
11、协程怎么取消?取消原理
答案
通过 Job.cancel () 取消协程;
协程在挂起点 会响应取消,抛出 CancellationException;作用域取消后,内部所有子协程全部级联取消。
12、协程异常怎么处理?CoroutineExceptionHandler
答案
- 单个协程内部用 try-catch 包裹挂起函数;
- 全局统一异常用 CoroutineExceptionHandler;
- launch 默认异常会崩溃;async 异常在 await 时才抛出。
13、协程结构化并发
结构化并发:子协程依附父协程作用域,父子生命周期绑定,自动取消、自动异常传递,杜绝协程泄漏。
核心三点
- 父子协程关系 协程有父子层级,子协程继承父Scope、Dispatcher、异常策略。
- 结构化取消 父作用域一取消,所有子协程跟着全部级联取消,不用手动一个个解绑,天然防内存泄漏。
- 异常自动传播子协程抛异常,会向上传给父协程,统一捕获处理,不会静默崩溃、丢异常。
14、协程的启动模式几种
四种枚举
CoroutineStart 一共四种:DEFAULT、LAZY、ATOMIC、UNDISPATCHED
逐个极简解释
1、DEFAULT(默认)
立刻根据调度器调度执行,排队进入对应线程池,标准常规启动。
2、LAZY(懒启动)
不会自动执行 ,需要手动调用 start() / join() 才开始跑;适合需要延迟触发、手动控制时机的场景。
3、ATOMIC
立即在当前线程原子式启动 ,不可取消;在挂起之前,无法被取消,执行到第一个挂起点才响应取消。
4、UNDISPATCHED
直接在当前线程立刻执行 ,不切调度器;直到遇到第一个 suspend 挂起点,之后才按 Dispatcher 调度切换线程。