網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
1.launch啟動(dòng)協(xié)程
fun main() = runBlocking { launch { delay(1000L) println("World!") } println("Hello") } fun main() { GlobalScope.launch { delay(1000L) println("World!") } println("Hello") Thread.sleep(2000L) } //輸出結(jié)果 //Hello //World!
上面是兩段代碼,這兩段代碼都是通過(guò)launch
啟動(dòng)了一個(gè)協(xié)程并且輸出結(jié)果也是一樣的。
第一段代碼中的runBlocking
是協(xié)程的另一種啟動(dòng)方式,這里先看第二段代碼中的launch
的啟動(dòng)方式;
- GlobalScope.launch
GlobalScope.launch
是一個(gè)擴(kuò)展函數(shù),接收者是CoroutineScope
,意思就是協(xié)程作用域,這里的launch
等價(jià)于CoroutineScope
的成員方法,如果要調(diào)用launch
來(lái)啟動(dòng)一個(gè)協(xié)程就必須先拿到CoroutineScope
對(duì)象。GlobalScope.launch
源碼如下
public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }
里面有三個(gè)參數(shù):
-
context: 意思是上下文,默認(rèn)是
EmptyCoroutineContext
,有默認(rèn)值就可以不傳,但是也可以傳遞Kotlin提供的Dispatchers
來(lái)指定協(xié)程運(yùn)行在哪一個(gè)線程中; -
start:
CoroutineStart
代表了協(xié)程的啟動(dòng)模式,不傳則默認(rèn)使用DEFAULT(根據(jù)上下文立即調(diào)度協(xié)程執(zhí)行),除DEFAULT
外還有其他類型:
LAZY:延遲啟動(dòng)協(xié)程,只在需要時(shí)才啟動(dòng)。
ATOMIC:以一種不可取消的方式,根據(jù)其上下文安排執(zhí)行的協(xié)程;
UNDISPATCHED:立即執(zhí)行協(xié)程,直到它在當(dāng)前線程中的第一個(gè)掛起點(diǎn);
-
block:
suspend
是掛起的意思,CoroutineScope.()
是一個(gè)擴(kuò)展函數(shù),Unit
是一個(gè)函數(shù)類似于Java的void
,那么suspend CoroutineScope.() -> Unit
就可以這么理解了:首先,它是一個(gè)掛起函數(shù),然后它還是CoroutineScope
類的成員或者擴(kuò)展函數(shù),參數(shù)為空,返回值類型為Unit
。
-
delay(): delay()方法從字面理解就是延遲的意思,在上面的代碼中延遲了1秒再執(zhí)行World,從源碼可以看出來(lái)它跟其他方法不一樣,多了一個(gè)
suspend
關(guān)鍵字
// 掛起 // ↓ 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) } } }
suspend
的意思就是掛起,被它修飾的函數(shù)就是掛起函數(shù), 這也就意味著delay()方法具有掛起和恢復(fù)的能力;
- Thread.sleep(2000L)
這個(gè)是休眠2秒,那么這里為什么要有這個(gè)呢?要解答這疑問(wèn)其實(shí)不難,將Thread.sleep(2000L)
刪除后在運(yùn)行代碼可以發(fā)現(xiàn)只打印了Hello
然后程序就結(jié)束了,World!
并沒有被打印出來(lái)。
為什么? 將上面的代碼轉(zhuǎn)換成線程實(shí)現(xiàn)如下:
fun main() { thread(isDaemon = true) { Thread.sleep(1000L) println("Hello World!") } }
如果不添加isDaemon = true
結(jié)果輸出正常,如果加了那么就沒有結(jié)果輸出。isDaemon
的加入后其實(shí)是創(chuàng)建了一個(gè)【守護(hù)線程】,這就意味著主線程結(jié)束的時(shí)候它會(huì)跟著被銷毀,所以對(duì)于將Thread.sleep
刪除后導(dǎo)致GlobalScope
創(chuàng)建的協(xié)程不能正常運(yùn)行的主要原因就是通過(guò)launch
創(chuàng)建的協(xié)程還沒開始執(zhí)行程序就結(jié)束了。那么Thread.sleep(2000L)
的作用就是為了不讓主線程退出。
另外這里還有一點(diǎn)需要注意:程序的執(zhí)行過(guò)程并不是按照順序執(zhí)行的。
fun main() { GlobalScope.launch { // 1 println("Launch started!") // 2 delay(1000L) // 3 println("World!") // 4 } println("Hello") // 5 Thread.sleep(2000L) // 6 println("Process end!") // 7 } /* 輸出結(jié)果: Hello Launch started! World! Process end! */
上面的代碼執(zhí)行順序是1、5、6、2、3、4、7,這個(gè)其實(shí)好理解,首先執(zhí)行1,然后再執(zhí)行5,執(zhí)行6的時(shí)候等待2秒,在這個(gè)等待過(guò)程中協(xié)程創(chuàng)建完畢了開始執(zhí)行2、3、4都可以執(zhí)行了,當(dāng)2、3、4執(zhí)行完畢后等待6執(zhí)行完畢,最后執(zhí)行7,程序結(jié)束。
2.runBlocking啟動(dòng)協(xié)程
fun main() { runBlocking { // 1 println("launch started!") // 2 delay(1000L) // 3 println("World!") // 4 } println("Hello") // 5 Thread.sleep(2000L) // 6 println("Process end!") // 7 }
上面這段代碼只是將GlobalScope.launch
改成了runBlocking
,但是執(zhí)行順序卻完全不一樣,它的執(zhí)行順訊為代碼順序1~7,這是因?yàn)?code>runBlocking是帶有阻塞屬性的,它會(huì)阻塞當(dāng)前線程的執(zhí)行。這是它跟launch
的最大差異。
runBlocking
與lanuch
的另外一個(gè)差異是GlobalScope
,從代碼中可以看出runBlocking
并不需要這個(gè),這點(diǎn)可以從源碼中分析
public actual fun <T> runBlocking( context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { ... }
頂層函數(shù):類似于Java中的靜態(tài)函數(shù),在Java中常用與工具類,例如StringUtils.lastElement();
runBlocking
是一個(gè)頂層函數(shù),因此可以直接使用它;在它的第二個(gè)參數(shù)block
中有一個(gè)返回值類型:T,它剛好跟runBlocking
的返回值類型是一樣的,因此可以推測(cè)出runBlocking
是可以有返回值的
fun main() { val result = test(1) println("result:$result") } fun test(num: Int) = runBlocking { return@runBlocking num.toString() } //輸出結(jié)果: //result:1
但是,Kotlin在文檔中注明了這個(gè)函數(shù)不應(yīng)該從協(xié)程中使用。它的設(shè)計(jì)目的是將常規(guī)的阻塞代碼與以掛起風(fēng)格編寫的庫(kù)連接起來(lái),以便在主函數(shù)和測(cè)試中使用。 因此在正式環(huán)境中這種方式最好不用。
3.async啟動(dòng)協(xié)程
在 Kotlin 當(dāng)中,可以使用 async{} 創(chuàng)建協(xié)程,并且還能通過(guò)它返回的句柄拿到協(xié)程的執(zhí)行結(jié)果。
fun main() = runBlocking { val deferred = async { 1 + 1 } println("result:${deferred.await()}") } //輸出結(jié)果: //result:2
上面的代碼啟動(dòng)了兩個(gè)協(xié)程,啟動(dòng)方式是runBlocking
和async
,因?yàn)?code>async的調(diào)用需要一個(gè)作用域,而runBlocking
恰好滿足這個(gè)條件,GlobalScope.launch
也可以滿足這個(gè)條件但是GlobalScope
也不建議在生產(chǎn)環(huán)境中使用,因?yàn)?code>GlobalScope 創(chuàng)建的協(xié)程沒有父協(xié)程,GlobalScope
通常也不與任何生命周期組件綁定。除非手動(dòng)管理,否則很難滿足我們實(shí)際開發(fā)中的需求。
上面的代碼多了一個(gè)deferred.await()
它就是獲取最終結(jié)果的關(guān)鍵。
public fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred<T> { val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyDeferredCoroutine(newContext, block) else DeferredCoroutine<T>(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }
async
和launch
一樣也是一個(gè)擴(kuò)展函數(shù),也有三個(gè)參數(shù),和launch
的區(qū)別在于兩點(diǎn):
-
block的函數(shù)類型:
launch
返回的是Unit
類型,async
返回的是泛型T
-
返回值不同:
launch
返回的是Job
,async
返回的是Deffered<T>
,而async
可以返回執(zhí)行結(jié)果的關(guān)鍵就在這里。
啟動(dòng)協(xié)程的三種方式都講完了,這里存在一個(gè)疑問(wèn),launch
和async
都有返回值,為什么async
可以獲取執(zhí)行結(jié)果,launch
卻不行?
這主要跟launch
的返回值有關(guān),launch
的返回值Job
代表的是協(xié)程的句柄,而句柄并不能返回協(xié)程的執(zhí)行結(jié)果。
句柄: 句柄指的是中間媒介,通過(guò)這個(gè)中間媒介可以控制、操作某樣?xùn)|西。舉個(gè)例子,door handle 是指門把手,通過(guò)門把手可以去控制門,但 door handle 并非 door 本身,只是一個(gè)中間媒介。又比如 knife handle 是刀柄,通過(guò)刀柄可以使用刀。
協(xié)程的三中啟動(dòng)方式區(qū)別如下:
- launch:無(wú)法獲取執(zhí)行結(jié)果,返回類型Job,不會(huì)阻塞;
- async:可獲取執(zhí)行結(jié)果,返回類型Deferred,調(diào)用await()會(huì)阻塞不調(diào)用則不會(huì)但也無(wú)法獲取執(zhí)行結(jié)果;
- runBlocking:可獲取執(zhí)行結(jié)果,阻塞當(dāng)前線程的執(zhí)行,多用于Demo、測(cè)試,官方推薦只用于連接線程與協(xié)程。
原文鏈接:https://juejin.cn/post/7171981069720223751
相關(guān)推薦
- 2022-03-16 .net6環(huán)境下使用RestSharp請(qǐng)求GBK編碼網(wǎng)頁(yè)亂碼的解決方案_實(shí)用技巧
- 2022-08-05 Redis實(shí)現(xiàn)分布式鎖的五種方法詳解_Redis
- 2022-07-13 File類的基本運(yùn)用、查找、刪除
- 2022-05-10 FactoryBean配置文件定義的 類型 調(diào)用時(shí)返回 不同的類型
- 2022-11-23 Python?hashlib模塊與subprocess模塊使用詳細(xì)介紹_python
- 2023-02-23 Redis的setNX分布式鎖超時(shí)時(shí)間失效?-1問(wèn)題及解決_Redis
- 2022-09-22 Python 短路運(yùn)算符
- 2023-05-16 Golang的鎖機(jī)制使用及說(shuō)明_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支