(21)
/**
* 协程与线程的关系
*
* 协程上下文与分发器
*
* 协程总是会在某个上下文中执行,这个上下文实际上是由CoroutineContext类型的一个实例来表示的,
* 该实例是由Kotlin标准库定义的
*
* 协程上下文本质上是各种元素所构成的一个集合,其中,主要的元素包括协程的job,以及分发器
*
* 所谓的分发器,其主要功能就是确定由哪个线程来执行我们所指定的协程代码
*
* 协程上下文包含了一个协程分发器,协程分发器确定了到底由哪个线程或者线程池来执行我们所指定的协程。
* 协程分发器可以将协程的执行限制
* 到一个具体的指定的线程,也可以将协程的执行分发到一个线程池中,由线程池的某个线程来执行
* 我们所指定的协程,还可以不加限制地去执行我们所指定的协程代码(在这种情况下,我们所指定的
* 协程代码到底是由哪个线程或者线程池来执行的是不确定的,它需要根据程序的实际执行
* 情况来确定;这种方式的协程分发器在一般的开发中使用较少,它只用在一些极为特殊的情况下)
*
* 协程分发器(CoroutineDispatcher)
*
* 所有的协程构建器(Coroutine builder)如launch和async都会接受一个可选的CoroutineContext参数,
* 该参数可以显式的指定新协程所
* 运行的分发器以及其他上下文元素。
*
*/
fun main() = runBlocking<Unit> {
/**
* 当通过launch来启动协程并且不指定协程分发器时,它会继承启动它的那个CoroutineScope的
* 上下文与分发器。对于该实例来说,他会
* 继承runBlocking的上下文,而runBlocking运行在main线程中
*/
launch {
println("No params, thread: ${Thread.currentThread().name}")
//运行结果: No params, thread: main
}
/**
* Dispatchers.Unconfined是一种很特殊的协程分发器,他在该实例中于是运行在main线程中,
* 但实际上,其运行机制与不指定协程分发器
* 时是完全不同的
*/
launch(Dispatchers.Unconfined) {
println("Dispatchers.Unconfined, thread: ${Thread.currentThread().name}")
//运行结果: Dispatchers.Unconfined, thread: main
}
/**
* Dispatchers.Default是默认的协程分发器,当协程是通过GlobalScope来启动的时候,他会使用
* 该默认的分发器来启动协程,他会使用
* 一个后台的共享线程池来运行我们的协程代码。因此,launch(Dispatchers.Default)
* 等价于GlobalScope.launch{ }
*/
launch(Dispatchers.Default) {
println("Dispatchers.Default, thread: ${Thread.currentThread().name}")
//运行结果:Dispatchers.Default, thread: DefaultDispatcher-worker-1
}
/**
* Executors.newSingleThreadExecutor().asCoroutineDispatcher()创建一个单线程的线程池,
* 该线程池的线程用来执行我们所指定的协程代码;在实际开发中,使用专门的线程来执行协程代码代价
* 是非常高的,因此在协程代码执行完毕之后,我们必须要释放相应的资源。
* 这里就需要使用close方法来关闭相应的协程分发器,从而释放资源;亦可以将该协程分发器存储到
* 一个顶层变量中,以便在程序的其他地方进行复用。
*/
val thread = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
launch(thread) {
println("Single thread executors,thread: ${Thread.currentThread().name}")
//运行结果:Single thread executors,thread: pool-1-thread-1
thread.close()
}
}
Dispatchers.Unconfined, thread: main
Dispatchers.Default, thread: DefaultDispatcher-worker-1
Single thread executors,thread: pool-1-thread-1
No params, thread: main
(22)
/**
* Dispatchers.Unconfined
*
* Dispatchers.Unconfined协程分发器会在调用者线程中去启动协程,但仅仅会持续到第一个挂起点,
* 当挂起点结束后程序恢复运行时,他会继续协程代码的执行。但这时执行协程的线程是由之前
* 所调用的挂起函数所决定的(这个挂起函数是由哪个线程来执行的,那么后面的代码
* 就会由这个线程来执行)。Dispatchers.Unconfined协程分发器适用于这样的一些线程:
* 它既不会消耗CPU时间,同时也不会更新任何共享的数据(特定于具体的线程)
*
* Dispatchers.Unconfined是一种高级的机制,他对于某些特殊的情况是很有帮助作用的:
* 协程的分发是不需要的,或者会产生意料之外的副作用这是因为协程中的操作必须要立刻执行。
*
* 我们在日常的代码编写中,几乎不会用到Dispatchers.Unconfined这种协程分发器。
*/
fun main() = runBlocking<Unit> {
launch(Dispatchers.Unconfined) {
//Dispatchers.Unconfined, thread: main
println("Dispatchers.Unconfined, thread: ${Thread.currentThread().name}")
delay(300)
//Dispatchers.Unconfined, thread: kotlinx.coroutines.DefaultExecutor
println("Dispatchers.Unconfined, thread: ${Thread.currentThread().name}")
}
launch {
//No params, thread: main
println("No params, thread: ${Thread.currentThread().name}")
delay(2000)
//No params, thread: main
println("No params, thread: ${Thread.currentThread().name}")
}
}
Dispatchers.Unconfined, thread: main
No params, thread: main
Dispatchers.Unconfined, thread: kotlinx.coroutines.DefaultExecutor
No params, thread: main
(23)
/**
* Job的使用方式以及在Context中的具体使用
*
* 协程的Job是归属于上下文的一部分,Kotlin为我们提供了一种简洁的手段来通过协程上下文
* 获取到协程自身的Job对象
*
* coroutineContext[Job]
*/
fun main() = runBlocking<Unit> {
val job: Job? = coroutineContext[Job]
println(job)
}
BlockingCoroutine{Active}@443b7951
(24)
/**
* 关于父子协程之间的关系
*
* 当一个协程是通过另一个协程的CoroutineScope(协程作用域)来启动的,那么这个协程
* 就会通过CoroutineScope.coroutineContext来继承
* 其上下文信息,同时,新协程的job就会成为父协程job的一个孩子;
* 当父协程被取消执行时,该父协程的所有孩子都会通过递归的方式一并取消执行
*
* 特例情况:当我们使用GlobalScope来启动协程时,对于启动的新协程来说,其job是没有父job的。
* 因此,他就不会绑定其所启动的那个范围上,故其可以独立执行(这是一种特殊情况)
*/
fun main() = runBlocking {
val request = launch {
GlobalScope.launch {
println("job1: hello")
delay(1000)
println("job1: world")
}
launch {
println("job2: hello")
delay(1000)
println("job2: world")
}
}
delay(500)
request.cancel()
delay(1000)
println("welcome")
}
job1: hello
job2: hello
job1: world
welcome
(25)
/**
* 对于父子协程来说,父协程总是会等待其所有的子协程的完成。
* 对于父协程来说,它不必显式的去追踪由它所启动的所有子协程,同时也不必
* 调用Job.join方法来等待子协程的完成。
*/
fun main() = runBlocking {
val request = launch {
repeat(5) {
launch {
delay((it + 1) * 100L)
println("Coroutine $it 执行完毕")
}
}
println("hello")
}
request.join()
println("world")
}
hello
Coroutine 0 执行完毕
Coroutine 1 执行完毕
Coroutine 2 执行完毕
Coroutine 3 执行完毕
Coroutine 4 执行完毕
world
(26)
/**
* CoroutineName上下文元素可以让我们对协程命名,以便更好的输出可读性好的日志调试信息
*/
private fun log(logMessage: String) = println("[${Thread.currentThread().name}] $logMessage")
fun main() = runBlocking(CoroutineName("main")) {
log("hello")
val value1 = async(CoroutineName("Coroutine1")) {
delay(800)
log("coroutine1 log")
30
}
val value2 = async(CoroutineName("coroutine2")) {
delay(1000)
log("coroutine2 log")
5
}
log("Result is ${value1.await() * value2.await()}")
}
[main] hello
[main] coroutine1 log
[main] coroutine2 log
[main] Result is 150
(27)
/**
* ThreadLocal 相关
*/
val threadLocal = ThreadLocal<String>()
fun main() = runBlocking<Unit> {
threadLocal.set("hello")
println("pre main, current thread : ${Thread.currentThread()}, thread local value : ${threadLocal.get()}")
val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "world")) {
println("launch start, current thread : ${Thread.currentThread()}, thread local value : ${threadLocal.get()}")
yield()
println("after yield, current thread : ${Thread.currentThread()}, thread local value : ${threadLocal.get()}")
}
job.join()
println("post main, current thread : ${Thread.currentThread()}, thread local value : ${threadLocal.get()}")
}
pre main, current thread : Thread[main,5,main], thread local value : hello
launch start, current thread : Thread[DefaultDispatcher-worker-1,5,main], thread local value : world
after yield, current thread : Thread[DefaultDispatcher-worker-1,5,main], thread local value : world
post main, current thread : Thread[main,5,main], thread local value : hello
(28)
/**
* 直接返回一个集合的做法有两个特点:
*
* 1.方法本身是阻塞的,即主线程会进到该方法中执行,一直到执行到该方法返回为止。
* 2.集合本身是一次性返回给调用端的,即集合中的元素均已经获取到后才一同返回给调用端。
*/
private fun myMethod(): List<String> = listOf("hello", "world", "hello world")
fun main() {
myMethod().forEach { println(it) }
}
hello
world
hello world
(29)
/**
* Sequence(序列)
*
* 如果在获取到每一个元素时都需要执行一定的计算,这种计算是一种阻塞行为,将计算后的多个结果返回给调用端
*/
private fun myMethod(): Sequence<Int> = sequence {
for (i in 100..105) {
Thread.sleep(1000)
yield(i)
}
}
fun main() {
myMethod().forEach { println(it) }
}
100
101
102
103
104
105