关于Kotlin协程的思考--阻塞和挂起的区别

624 阅读2分钟

kotlin协程的作用

kotlin协程是用来简化异步程序设计的。
通常我们编写异步程序,面临两个问题。
一是异步程序结果的获取阻塞主流程。
二是异步程序结果的获取脱离主流程。

异步程序结果的获取阻塞主流程

fun useFuture() {
    val executor = Executors.newCachedThreadPool()
    val future = executor.submit<String> {
        Thread.sleep(1000)
        "result"
    }
    val result = future.get()//主流程阻塞在这里了
    println(result)
}

异步程序结果的获取脱离主流程

fun useCompletableFuture() {
    val completableFuture = CompletableFuture.supplyAsync {
        Thread.sleep(1000)
        "1"
    }.thenAccept {//通过回调获取异步程序的结果,脱离了主调用流程  
        println(it)
    }
    println("2")

    completableFuture.join()//避免程序提前结束
}

kotlin对异步程序结果的获取

我们这里获取异步程序的结果,没有使用回调,而是在异步代码的下方(在主流程中),所以没有脱离主调用流程。 而且defer.await是非阻塞式挂起,并没有阻塞。

GlobalScope.launch {
    val defer = async(Dispatchers.IO) {
        println("1")
        Thread.sleep(1000)
        println("2")
        "3"
    }
    println(defer.await())
}

阻塞式地等待结果和使用挂起非阻塞式等待结果的区别

如果我们简单地来看,不管是阻塞式地还是非阻塞的,程序都停在等待结果的地方,好像没有什么区别

fun useFuture() {
    val executor = Executors.newCachedThreadPool()
    val future = executor.submit<String> {
        Thread.sleep(1000)
        "result"
    }
    val result = future.get()//主流程阻塞在这里了,等待
    println(result)
}
GlobalScope.launch {
    val defer = async(Dispatchers.IO) {
        println("1")
        Thread.sleep(1000)
        println("2")
        "3"
    }
    println(defer.await())//等待结果
}

但是我们看看下面的例子

阻塞式

val newSingleThreadContext = newSingleThreadContext("single thread")

GlobalScope.launch(newSingleThreadContext) {
    "1".log()
    Thread.sleep(5000)
    "2".log()
}

GlobalScope.launch(newSingleThreadContext) {
    "3".log()
}

useful info single thread @coroutine#1 256826006 1 //这里执行后停留5秒
useful info single thread @coroutine#1 256826006 2
useful info single thread @coroutine#2 256826006 3

非阻塞式

val newSingleThreadContext = newSingleThreadContext("single thread")

GlobalScope.launch(newSingleThreadContext) {
    "1".log()
    delay(5000)
    "2".log()
}

GlobalScope.launch(newSingleThreadContext) {
    "3".log()
}

useful info single thread @coroutine#1 793035649 1
useful info single thread @coroutine#2 793035649 3 //这里执行后停留5秒
useful info single thread @coroutine#1 793035649 2

如果线程被阻塞了,那么线程就不能干任何事情了
但是如果使用非阻塞式地挂起,那么协程被挂起过后,线程还可以去执行另一个协程