Kotlin协程第一个示例剖析
Gradle工程
这里新建一个Gradle工程
默认的gradle的配置如下
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm'
}
group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'//指定Kotlin的基础依赖包和协程相关的依赖包
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
示例剖析
fun main() {
//CoroutineScope.launch{}:
//CoroutineScope.launch{}是最常用的Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器。
//GlobalScope 协程构建器
GlobalScope.launch {
delay(1000)//此时不会阻塞线程,当指定的时间过后协程会恢复执行
println("Kotlin Coroutines")
}
println("Hello")
Thread.sleep(2000)
println("World")
}
RUN> ??????
Hello Kotlin Coroutines World Process finished with exit code 0
而看一下GlobalScope这个类,就是它的实现类
public object GlobalScope : CoroutineScope {
/**
* Returns [EmptyCoroutineContext].
*/
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
CoroutineScope.launch{}是最常用的Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器。
public suspend fun delay(timeMillis: Long)
/**
* Delays coroutine for a given time without blocking a thread and resumes it after a specified time.
*
* This suspending function is cancellable.
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
* immediately resumes with [CancellationException].
* There is a **prompt cancellation guarantee**. If the job was cancelled while this function was
* suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details.
*
* If you want to delay forever (until cancellation), consider using [awaitCancellation] instead.
*
* Note that delay can be used in [select] invocation with [onTimeout][SelectBuilder.onTimeout] clause.
*
* Implementation note: how exactly time is tracked is an implementation detail of [CoroutineDispatcher] in the context.
* @param timeMillis time in milliseconds.
*/
public suspend fun delay(timeMillis: Long) {
if (timeMillis <= 0) return // don't delay
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
// if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
if (timeMillis < Long.MAX_VALUE) {
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
}
Delays coroutine for a given time without blocking a thread and resumes it after a specified time. 会延迟协程指定的时间;但是此时并不会阻塞线程,而当指定的时间过后则协程又会恢复执行
修改示例
Thread.sleep(500)
fun main() {
//CoroutineScope.launch{}:
//CoroutineScope.launch{}是最常用的Coroutine builders,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器。
//GlobalScope 协程构建器
GlobalScope.launch {
delay(1000)//此时不会阻塞线程,当指定的时间过后协程会恢复执行
println("Kotlin Coroutines")
}
println("Hello")
Thread.sleep(500)//修改点
println("World")
}
RUN> ??????
Hello World
居然协程都木有输出,这是因为协程是依附于线程的,当线程都退出了,当然协程也不会执行了嘛,这点可以清楚的体会到协程的一个角色。
Kotlin线程使用技巧:
对于上面协程的效果其实可以用纯线程的方式来实现,这里来学习一下在Koltin中使用线程的一个标准姿势,跟Java还是有很大的区别的,如下:
import kotlin.concurrent.thread
fun main() {
thread {
Thread.sleep(1000)
println("Kotlin Coroutines")
}
println("Hello")
Thread.sleep(2000)
println("World")
RUN> ??????
Hello Kotlin Coroutines World Process finished with exit code 0
理解Kotlin的这种创建线程的方式,先看一下这个thread是怎么定义的
/**
* Creates a thread that runs the specified [block] of code.
*
* @param start if `true`, the thread is immediately started.
* @param isDaemon if `true`, the thread is created as a daemon thread. The Java Virtual Machine exits when
* the only threads running are all daemon threads.
* @param contextClassLoader the class loader to use for loading classes and resources in this thread.
* @param name the name of the thread.
* @param priority the priority of the thread.
*/
public fun thread(
start: Boolean = true,
isDaemon: Boolean = false,
contextClassLoader: ClassLoader? = null,
name: String? = null,
priority: Int = -1,
block: () -> Unit
): Thread {
val thread = object : Thread() {
public override fun run() {
block()
}
}
if (isDaemon)
thread.isDaemon = true
if (priority > 0)
thread.priority = priority
if (name != null)
thread.name = name
if (contextClassLoader != null)
thread.contextClassLoader = contextClassLoader
if (start)
thread.start()
return thread
}
居然是一个函数,该函数返回的是一个Thread对象
其中返回的Thread是为Java的
thread { Thread.sleep(1000) println("Kotlin Coroutines") }
使用了Kotlin的尾随闭包形式,大括号内的为
block: () -> Unit
的函数体为啥这样传递了之后我们的线程中的代码就能得到正常执行呢?
Creates a thread that runs the specified [block] of code. 创建一个线程并且运行所指定的代码块。
start - if true, the thread is immediately started. 如果它为真,该线程则会立马start,也就是开始执行嘛
isDaemon - if true, the thread is created as a daemon thread. The Java Virtual Machine exits when the only threads running are all daemon threads.
如果为true,则该线程 就会被创建为一个守护线程,而对于守护线程,如果所有正在运行的线程都是守护线程的话则JVM会退出
contextClassLoader - the class loader to use for loading classes and resources in this thread
类加载器用来加载这个线程的类和资源
再来看一下此方法的具体实现,就知道为啥这个方法有这样创建线程的功效了:
修改代码
fun main() {
//我们手动的来将start参数置为false,看下是否线程中的代码块就不会被执行了:
thread(start = false) {
Thread.sleep(1000)
println("Kotlin Coroutines")
}
println("Hello")
Thread.sleep(2000)
println("World")
}
RUN > ??????
Hello World Process finished with exit code 0
我们也可以以Java的思路将其手动启动
fun main() { val t = thread(start = false) { Thread.sleep(1000) println("Kotlin Coroutines") } t.start() println("Hello") Thread.sleep(2000) println("World") }
RUN>
Hello Kotlin Coroutines World Process finished with exit code 0
但是!!!这不是一个正常使用Kotlin的姿势,如果用Java的思维来使用Kotlin这种是不对的,需要适应新的写法。