Kotlin的协程是什么?
-
Kotlin的协程在 JVM上是线程框架,但它的概念层不仅仅是线程框架
-
协程是一种在程序中的处理并发任务的方案;也是这种方案的一个组件; 可以理解为一个Adapter。
-
协程和线程属于一个层级的概念,是一种不同于线程的概念,用于解决并发
-
协程中不存在线程,也不存在并行(注意:并行和并发不是同一个概念) 并行:Parallelism:导致线程安全问题,如开两个线程给两个数组进行筛选处理 并发:Concurrency:如后台发请求,前端显示请求信息
-
Kotlin的协程不需要处理以上的问题,但依然存在java的线程安全问题,所有基于JVM的语言,Java,Scala,Groovy等都无法避免此问题
-
-
Kotlin For Java 的协程不同于广义的协程
协程的写法
-
开启协程和线程写法比较 例子:
//开启一个协程(非常规写法) GlobalScope.launch { print("线程的名称1:${Thread.currentThread().name}") } //开启并运行一个线程 Thread{ print("线程的名称2:${Thread.currentThread().name}") }.start() //KTX 的线程写法: thread { print("线程的名称3:${Thread.currentThread().name}") }可见的是,如果仅仅是开启一个后台任务,协程并没有语法上的优势,甚至于增加了拓展函数包之后,开启线程来运行一个后台任务更加的方便,但协程的优势并不是在此,而是在其的可挂起上和线程切换上。 下面的示例
IoCode()代表一个后台任务,uiCode()代表一个前台任务,于是在线程上我们就要这样子做线程切换:
thread {
ioCode1()
runOnUIThread{
uiCode1()
thread {
ioCode2()
runOnUiThread{
uiCode2()
}
}
}
}
可以看见的是缺点很明显,代码可读性低,写起来烧脑,且难以维护,套娃,代码难看。业务逻辑复杂后引起回调地狱。
以下用协程来实现:
- 首先指定运行的方法为挂起函数(加上关键字
suspend),并且给他们指定运行的线程(Dispatchers.IO,Dispatchers.Main等) - 将挂起函数在协程中运行
//指定该函数在IO线程执行
private suspend fun ioCode()= withContext(Dispatchers.IO){
//业务逻辑
}
//指定该函数在ui线程执行
private suspend fun uiCode()= withContext(Dispatchers.Main){
//前端业务逻辑
}
....
GlobalScope.launch {
ioCode()
uiCode()
}
otherCode()
//其他函数
执行的顺序,ioCode执行之后,uiCode才会执行,协程即GlobalScope.launch{}内的逻辑不会阻塞后面的代码(otherCode())的执行,协程会独立给予他们一个线程环境去运行,并且可以指定这个运行环境如下:
GlobalScope.launch(Dispatchers.Main) {
//该代码块内的环境为主线程
}
GlobalScope.launch(Dispatchers.IO) {
//该代码块内的环境为IO线程
}
那么问题来了,如果一个方法内需要执行的逻辑需要多次切换呢?这其实也很简单
suspend fun testFunc(){
withContext(Dispatchers.IO){
//IO线程内的操作
func1()
}
withContext(Dispatchers.Main){
//UI线程中的操作
func2()
}
withContext(Dispatchers.IO){
//IO线程内的操作
func3()
}
}
GlobalScope.launch(Dispatchers.Main) {
//该代码块内的环境为主线程
testFunc()
}
协程会保持的代码的时序性质,即示例代码中的func1,func2,fun3会依次执行,且会在我们设定的线程中运行。
总结一下:
1. 用lauch()开启一段协程,并指定运行环境,通常为Dispatchers.Main)
2. 把需要后台工作的函数写成suspend挂起函数,可以在内部使用withContext或者其他挂起函数来切换线程
3. 将挂起函数写在协程之中,一条线,优雅方便且读性强
协程的天然优势:性能
- 程序什么时候需要切换线程
-
工作比较耗时:放在后台
- 问题节点:如何判断该方法耗时还是不耗时? 很多我们不熟悉或者同学写的看不懂(垃圾)的代码无法判断其耗时或者不耗时,但我们没有条件去逐个测试其耗时性,或者有些方法耗时不稳定,难以测试。但有了协程,就避免了这个问题,所有有耗时的函数可以全写在协程之中,就避免了所有的非UI渲染的耗时操作,且不影响可读性。
- 这使用Java基本无法实现。
-
工作比较特殊:放在指定线程 —— 一般我们放在了主线程,如UI操作
-
协程 For JVM的概念
- 线程框架
- 可以用看上去同步的代码写出异步的操作
- 优势1:耗时函数自动后台,从而提高性能
- 优势2:线程的“自动切回”
协程和线程,线程和协程安全的关系?
suspend
- 并不是用来切线程的
- 仅仅是一个标记和提醒,这是一个挂起函数(语法层)