目录
协程
首先配置,在build.gradle中 配置:
plugins {
id 'java-library'
id 'kotlin'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.6.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3'
}
根据官方文档:协程是一个轻量级的线程框架(API)。为什么不直接使用java线程? 因为回调太多。
开始使用
1.Thread.sleep来阻塞
fun main() {
GlobalScope.launch { //在后台启动一个新的协程并继续
delay(1000) //--挂起函数,不会造成阻塞,但会挂起协程,只能使用在协程中
println("World!")
println("ThreadName : ${Thread.currentThread().name}")
}
println("Hello,")
println("ThreadName : ${Thread.currentThread().name}")
//协程已在等待时主线程还在继续,阻塞主线程2秒来保证jvm存活
//如果不睡眠,主线程走完就死了,协程也就没了
Thread.sleep(2000) //阻塞主线程2秒 保证其jvm的存活
}
运行结果:Hello,
ThreadName : main
World!
ThreadName : DefaultDispatcher-worker-1
如果我们把sleep去了,运行结果是:Hello,
ThreadName : main
看运行结果可以看出GlobalScope是创建了一个线程,所以我们可以使用之前的线程打到同样的效果:
fun main() {
thread{
Thread.sleep(1000)
println("World!")
println("ThreadName : ${Thread.currentThread().name}")
}
println("Hello,")
println("ThreadName : ${Thread.currentThread().name}")
}
运行结果:Hello,
ThreadName : main
World!
ThreadName : Thread-0
使用thread,我们外部就可以不用sleep了。
2.runBlocking阻塞主线程
fun main() {
GlobalScope.launch {
delay(1000)
println("World!")
println("ThreadName GlobalScope: ${Thread.currentThread().name}")
}
println("Hello,")
println("ThreadName : ${Thread.currentThread().name}")
// 这个表达式阻塞了主线程,我们延迟2秒来保证jvm的存活
runBlocking {
delay(2000)
println("ThreadName runBlocking : ${Thread.currentThread().name}")
}
}
运行结果:Hello,
ThreadName : main
World!
ThreadName GlobalScope: DefaultDispatcher-worker-1
ThreadName runBlocking: main
我们可以换一种简洁写法:
//把主线程包装成了一个kotlin的协程
fun main() = runBlocking<Unit> {//开始执行主协程
GlobalScope.launch {
delay(1000)
println("World!")
println("ThreadName GlobalScope: ${Thread.currentThread().name}")
}
println("Hello,")
println("ThreadName hello: ${Thread.currentThread().name}")
delay(2000)
println("ThreadName finally: ${Thread.currentThread().name}")
}
运行结果:Hello,
ThreadName hello: main
World!
ThreadName GlobalScope: DefaultDispatcher-worker-1
ThreadName finally: main
3.join来阻塞
fun main() = runBlocking<Unit> {//开始执行主协程
val job = GlobalScope.launch {
delay(1000)
println("World!")
println("ThreadName : ${Thread.currentThread().name}")
}
println("Hello,")
println("ThreadName : ${Thread.currentThread().name} hello")
job.join() //等待直到job这个子协程结束,才往下走。 是属于阻塞
println("complete")
}
运行结果:Hello,
ThreadName : main hello
World!
ThreadName : DefaultDispatcher-worker-2
complete
4.runBlocking中阻塞(2)
我们可以在执行操作所在的指定作用域内启动协程,而不是像通常使用GlobalScope那样启动。包括runBlocking在内的每个协程构建器都将CoroutineScope的实例添加到其代码块所在的作用域中。可以在这个作用域中启动协程,无需显式join,因为外部协程(示例中的runBlocking)。直到在其作用域中启动的所有协程都执行完毕后才会结束。
fun main() = runBlocking<Unit> {
launch {
delay(2000L)
println("World!")
println("ThreadName launch内的: ${Thread.currentThread().name}")
}
println("Hello,")
println("ThreadName launch之外的: ${Thread.currentThread().name} ")
}
输出结果:Hello,
ThreadName launch之外的: main
World!
ThreadName launch内的: main
5.作用域构建器
除了由不同的构建器提供协程作用域之外,还可以使用coroutineScope构建器声明自己的作用域。它会创建一个协程作用域且在所有已启动子协程执行完毕之前不会结束,遇到子协程(coroutineScope),不执行完子协程不会继续后面的代码执行
fun main() = runBlocking<Unit> {
coroutineScope {
launch {
delay(2000L)
println("World!")
println("ThreadName launch内的: ${Thread.currentThread().name}")
}
}
println("Hello,")
println("ThreadName launch之外的: ${Thread.currentThread().name} ")
}
输出结果:World!
ThreadName launch内的: main
Hello,
ThreadName launch之外的: main
这个示例的运行结果就比较明显,在runBlocking中的coroutineScope子协程运行完毕后,才会接着走下去(运行下去) 。下面我们来一个更明显的示例来看看:
fun main() = runBlocking<Unit> {
launch {
delay(200L)
println("Task from 200L")
println("ThreadName : ${Thread.currentThread().name}")
}
coroutineScope {
launch {
delay(500L)
println("Task from 500L")
println("ThreadName : ${Thread.currentThread().name}")
}
delay(100L)
println("Task from 100L")
println("ThreadName : ${Thread.currentThread().name}")
}
GlobalScope.launch {
delay(50L)
println("Task from 50L")
println("ThreadName : ${Thread.currentThread().name}")
}
println("Coroutine scope is over")
println("ThreadName : ${Thread.currentThread().name}")
}
运行结果:Task from 100L
ThreadName : main
Task from 200L
ThreadName : main
Task from 500L
ThreadName : main
Coroutine scope is over
ThreadName : main
运行结果我们就会有疑问了,为什么不会先输出 is over或者50L的呢?因为我们中间有使用一个子协程coroutineScope,所以要等待coroutineScope运行完毕之后才会执行后边的代码,所以按照lauch、coroutineScope中的delay挂起时间就是先输出100L,200L,500L。coroutineScope结束后没有执行50L的原因是咱们这篇的第一点,在主线程执行结束后,jvm没了,协程自然也就没了。所以就没有执行。如果我们把GlobalScope换成lauch或者再来一个子协程coroutineScope都是会输出50L的,但是运行时序不同,lauch的运行结果会先输出over在输出50L,coroutineScope则是先50L。上代码和运行结果把,比较方便理解:
lauch:
fun main() = runBlocking<Unit> {
launch {
delay(200L)
println("Task from 200L")
println("ThreadName : ${Thread.currentThread().name}")
}
coroutineScope {
launch {
delay(500L)
println("Task from 500L")
println("ThreadName : ${Thread.currentThread().name}")
}
delay(100L)
println("Task from 100L")
println("ThreadName : ${Thread.currentThread().name}")
}
launch {
delay(50L)
println("Task from 50L")
println("ThreadName : ${Thread.currentThread().name}")
}
println("Coroutine scope is over")
println("ThreadName : ${Thread.currentThread().name}")
}
运行结果:Task from 100L
ThreadName : main
Task from 200L
ThreadName : main
Task from 500L
ThreadName : main
Coroutine scope is over
ThreadName : main
Task from 50L
ThreadName : main
coroutineScope:
fun main() = runBlocking<Unit> {
launch {
delay(200L)
println("Task from 200L")
println("ThreadName : ${Thread.currentThread().name}")
}
coroutineScope {
launch {
delay(500L)
println("Task from 500L")
println("ThreadName : ${Thread.currentThread().name}")
}
delay(100L)
println("Task from 100L")
println("ThreadName : ${Thread.currentThread().name}")
}
coroutineScope{
launch {
delay(50L)
println("Task from 50L")
println("ThreadName : ${Thread.currentThread().name}")
}
}
println("Coroutine scope is over")
println("ThreadName : ${Thread.currentThread().name}")
}
运行结果:Task from 100L
ThreadName : main
Task from 200L
ThreadName : main
Task from 500L
ThreadName : main
Task from 50L
ThreadName : main
Coroutine scope is over
ThreadName : main
最后这两个例子可以说是串起了这篇的大多知识点。 共勉,一起加油