Android面试题之Kotlin协程到底是什么?它是线程吗?

简介: 本文探讨了协程与线程的区别,指出协程并非线程,而是轻量级的线程替代。协程轻量体现在它们共享调用栈,内存占用少,仅需几个KB。协程切换发生在用户态,避免了昂贵的内核态切换。在Kotlin中,协程通过Continuation对象实现上下文保存,允许高效并发执行,而不会像线程那样消耗大量资源。通过`runBlocking`和`launch`示例展示了协程的非阻塞挂起特性。总结来说,协程的轻量主要源于内存占用少、切换开销低和高并发能力。

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

协程是什么?

协程是线程吗?是线程池的线程?是轻量级的线程?

实际上,可以非常肯定的说,协程不是线程!

我们反过来想,如果协程是线程,那么咱们怎么让一个线程挂起呢?

那既然协程不是线程,那又为什么常说协程是轻量级的线程呢?协程轻在哪呢?

"轻量级"的线程

Kotlin中的协程经常被称为“轻量级线程”,这是相对于传统的线程模型而言的。为了更好地理解这一点,我们需要从内存占用、任务切换、JVM内存模型等多方面进行剖析。

1. 轻量级的原因

1.1 内存占用

  • 线程: 每个线程在创建时分配一定数量的栈内存(默认大约1MB)。如果系统启动大量线程,则会消耗大量内存,可能导致系统资源枯竭。
  • 协程: 协程是运行在现有线程中的,它们不需要单独的栈内存,而是共享调用栈。这样协程仅需要少量内存开销,通常每个协程只占用几个KB。这使得同一线程可以管理和运行大量协程,不受传统线程数量限制。

1.2 任务切换

  • 线程切换: 线程切换由操作系统管理,涉及到用户态和内核态之间的切换,代价较高,需要保存和恢复CPU寄存器、程序计数器、内存栈等。
  • 协程切换: 协程切换在用户态完成,不涉及内核态切换,只是切换函数的上下文,代价相对低很多。

2. 内存模型

2.1 线程内存模型

在JVM中,每个线程都有自己独立的线程栈,每个栈帧包含局部变量、操作数栈和相关信息。当线程被挂起时,所有这些信息必须保存并在重新调度时恢复。

2.2 协程内存模型

协程的栈帧通常是堆上的对象,当协程挂起时,不需要切换线程,只是函数调用的上下文发生变化,把协程状态保存到堆中。这种模型使得内存利用更加高效和灵活。

2.3 协程堆栈帧

协程在挂起时,会将当前的堆栈帧转换为对象并存储在堆中。这个对象包含了所有当前帧的局部变量、挂起点以及其他必要信息。恢复时,这个对象重新转换为堆栈帧并继续执行。

2.4 Continuation

Kotlin中的挂起函数实质上会被编译器转换成带有回调的 Continuation 对象。该对象包含两个主要部分:

  • 上下文(Continuation Context):绑定的执行环境。
  • 恢复逻辑(Resume Logic):保存和处理挂起点的逻辑。
interface Continuation<in T> {
    val context: CoroutineContext
    fun resumeWith(result: Result<T>)
}

3. 执行机制

3.1 线程

线程由操作系统调度,执行时获得CPU时间片,可能被抢占。这一过程完全由操作系统管理,且每次线程切换都会导致上下文切换,引入显著的开销。

3.2 协程

协程仅占用一个线程的部分时间,是由协程库(例如 kotlinx.coroutines)管理。一个线程可以执行多个协程,只要它们异步工作并时常挂起和恢复。这大大减少了切换开销,改善性能。

4. 协程例子

import kotlinx.coroutines.*

fun main() = runBlocking {
    repeat(100_000) {
        launch {
            delay(1000L)
            println("Coroutine $it is done")
        }
    }
}

在这个例子中,启动了10万协程,但内存占用远远小于10万个线程,且切换效率高。这是因为:

  • 非阻塞挂起: delay 使得协程挂起,但不阻塞线程,使得同一线程可以继续处理其他协程。
  • 上下文保存和恢复: 协程的上下文切换只需要保存和恢复堆上的对象状态,代价低。

由于协程不阻塞线程,上面的例子中,日志几乎是同时打印的

总结

Kotlin 协程的,主要原因包括:

  1. 内存占用更少:协程不需要独立的栈内存,而是共享调用栈。
  2. 低切换开销:协程切换在用户态完成,无需与操作系统交互,开销小。
  3. 高并发模型:在同一线程上可以高效地运行大量协程,不受传统线程创建管理的限制。

欢迎关注我的公众号AntDream查看更多精彩文章!

目录
相关文章
|
1天前
|
网络协议 算法 安全
小米安卓春招面试一面
小米安卓春招面试一面
17 3
|
1天前
|
缓存 网络协议 Java
Android面试题之Java网络通信基础知识
Socket是应用与TCP/IP通信的接口,封装了底层细节。网络通信涉及连接、读写数据。BIO是同步阻塞,NIO支持多路复用(如Selector),AIO在某些平台提供异步非阻塞服务。BIO示例中,服务端用固定线程池处理客户端请求,客户端发起连接并读写数据。NIO的关键是Selector监控多个通道的事件,减少线程消耗。书中推荐《Java网络编程》和《UNIX网络编程》。关注公众号AntDream了解更多。
11 2
|
1天前
|
数据挖掘 调度 开发者
Python并发编程的艺术:掌握线程、进程与协程的同步技巧
并发编程在Python中涵盖线程、进程和协程,用于优化IO操作和响应速度。`threading`模块支持线程,`multiprocessing`处理进程,而`asyncio`则用于协程。线程通过Lock和Condition Objects同步,进程使用Queue和Pipe通信。协程利用异步事件循环避免上下文切换。了解并发模型及同步技术是提升Python应用性能的关键。
|
2天前
|
Java 开发者 计算机视觉
探索Python中的并发编程:线程与协程
本文将深入探讨Python中的并发编程,重点比较线程和协程的工作机制、优缺点及其适用场景,帮助开发者在实际项目中做出更明智的选择。
|
2天前
|
XML JSON Java
Android面试题 之 网络通信基础面试题
序列化对比:Serializable码流大、性能低;XML人机可读但复杂;JSON轻量、兼容性好但空间消耗大;ProtoBuff高效紧凑。支持大量长连接涉及系统限制调整、缓冲区优化。select/poll/epoll是IO多路复用,epoll在高连接数下性能更优且支持边缘触发。水平触发持续通知数据,边缘触发仅通知新数据。直接内存减少一次拷贝,零拷贝技术如sendfile和MMAP提升效率。关注公众号&quot;AntDream&quot;了解更多技术细节。
7 1
|
2天前
|
Android开发 Kotlin
Android面试题 之 Kotlin DataBinding 图片加载和绑定RecyclerView
本文介绍了如何在Android中使用DataBinding和BindingAdapter。示例展示了如何创建`MyBindingAdapter`,包含一个`setImage`方法来设置ImageView的图片。布局文件使用`&lt;data&gt;`标签定义变量,并通过`app:image`调用BindingAdapter。在Activity中设置变量值传递给Adapter处理。此外,还展示了如何在RecyclerView的Adapter中使用DataBinding,如`MyAdapter`,在子布局`item.xml`中绑定User对象到视图。关注公众号AntDream阅读更多内容。
9 1
|
4天前
|
Android开发
Android面试题之activity启动流程
该文探讨了Android应用启动和Activity管理服务(AMS)的工作原理。从Launcher启动应用开始,涉及Binder机制、AMS回调、进程创建、Application和Activity的生命周期。文中详细阐述了AMS处理流程,包括创建ClassLoader、加载APK、启动Activity的步骤,以及权限校验和启动模式判断。此外,还补充了activity启动流程中AMS的部分细节。欲了解更多内容,可关注公众号“AntDream”。
9 1
|
4天前
|
vr&ar 数据库 Android开发
Android面试题之ActivityManagerService的启动流程
本文探讨了Android系统的SystemServer启动过程,包括创建SystemContext、引导服务、启动各类核心服务以及AMS的启动和初始化。AMS负责管理activity、广播队列、provider等,并设置SystemProcess,安装系统Provider。当AMS调用SystemReady时,系统UI准备启动,启动Launcher。文中还对比了init、zygote和system_server进程的角色。最后推荐了两本关于Android内核剖析的书籍:柯元旦教授的《Android内核剖析》和罗升阳的《Android系统源代码情景分析》。关注公众号AntDream获取更多内容。
6 0
|
5天前
|
JSON 安全 调度
Android面试题之Kotlin协程一文搞定
本文介绍了协程的基础知识,强调它是轻量级线程,用于处理耗时任务而不阻塞主线程,确保主线程安全。协程特点包括使异步逻辑同步化,并允许函数挂起和恢复。挂起函数由`suspend`关键字标识,只能在协程内部调用。挂起与阻塞的主要区别在于挂起不会导致主线程ANR。 结构化并发和协程作用域(如`CoroutineScope`、`GlobalScope`、`MainScope`等)提供了任务管理,文章还探讨了并发、启动模式、协程取消、超时任务以及资源释放等主题。
13 0
|
1月前
|
传感器 Android开发 开发者
构建高效Android应用:Kotlin的协程与Flow
【4月更文挑战第26天】随着移动应用开发的不断进步,开发者寻求更简洁高效的编码方式以应对复杂多变的业务需求。在众多技术方案中,Kotlin语言凭借其简洁性和强大的功能库逐渐成为Android开发的主流选择。特别是Kotlin的协程和Flow这两个特性,它们为处理异步任务和数据流提供了强大而灵活的工具。本文将深入探讨如何通过Kotlin协程和Flow来优化Android应用性能,实现更加流畅的用户体验,并展示在实际开发中的应用实例。