協程經過delay(timeMillis)實現掛起,線程經過sleep(timeMillis)實現休眠。可是掛起和休眠存着差別性api
協程掛起與線程休眠bash
相同點:微信
1.都能達到堵塞的目的;
2.在該狀態下(掛起/休眠)都能被終止執行(取消/中斷);
3.取消/中斷時都會拋出異常
複製代碼
不一樣點:async
1.線程休眠會直接堵塞當前線程,該線程沒法再執行其它操做,可是協程掛起不會堵塞當前線程,線程上的其它協程能夠繼續運行;
2.delay操做只能在協程環境使用,sleep在協程環境和普通線程環境均可使用
3.線程中斷拋出的是InterruptedException異常,協程中斷拋出的是CancellationException異常
複製代碼
協程的取消與線程的中斷,2者十分相似 :spa
比較 | 調用api | 能被直接取消的狀態 | 事務處理時 | 拋出的異常 |
---|---|---|---|---|
協程 | cancel() | delay | 沒法被直接取消 | CancellationException |
線程 | interrupt() | sleep | 沒法被直接取消 | InterruptedException |
相同點:線程
1.都是須要處於掛起/休眠狀態,纔可以直接取消/中斷;code
2.處於事務處理時,沒法直接被取消/中斷;cdn
不一樣點在於:協程
1.協程存在父協程的概念,可是線程沒有啥所謂的父線程。取消父協程後,會自動取消其全部的子協程;事務
2.協程在掛起時被取消,會拋出CancellationException異常,線程在休眠時被中斷,會拋出InterruptedException
該協程job在執行cancel的時候,處於delay狀態,可以被直接取消
fun cancel() = runBlocking {
val job = launch {
println("1")
delay(3000)
//被cancel後,下邊再也不執行
println("2")
}
delay(100)
job.cancel()
println("3")
}
打印出:
1
3
複製代碼
該線程thread在執行interrupt的時候,處於sleep狀態,可以被直接中斷
fun threadInterrupt() {
val thread = thread {
println("1")
Thread.sleep(1 * 1000)
println("2")
}
//拋出InterruptedException但不影響後面執行
thread.interrupt()
Thread.sleep(3 * 1000)
println("3")
}
打印出:
1
3
複製代碼
協程在執行cancel()後,內部的擴展屬性isActive會置爲false,代碼中能夠結合該變量跳出事務處理的流程。 以下例子中的while()判斷,假設沒有結合iaActive,由於當前協程處於事務處理,不是delay掛起狀態,沒法直接被cancel, 須要等待事務處理完畢。
爲了達到cancel後儘早結束協程,能夠結合isActive進行判斷。
fun cancel() = runBlocking {
val job = launch(Dispatchers.Default) {
var nextPrintTime = System.currentTimeMillis()
var i = 0
//isActive是協程擴展屬性,cancel()後變爲false
while (i < 5 && isActive) {
// 一個執行計算的循環,只是爲了佔用 CPU
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
// 每秒打印消息兩次
nextPrintTime += 500L
}
}
}
delay(100)
job.cancel()
println("end")
}
結合isActive進行中斷判斷,打印出:
job: I'm sleeping 0 ... end 假設沒有結合isActive進行判斷,打印出: job: I'm sleeping 0 ...
end
job: I'm sleeping 1 ... job: I'm sleeping 2 ...
job: I'm sleeping 3 ... job: I'm sleeping 4 ...
複製代碼
線程使用interrupt()結合isInterrupted變量,達到終端操做的目的,效果與分析同協程cancel()結合isActive相似:
fun threadInterrupt() {
val thread = thread {
var nextPrintTime = System.currentTimeMillis()
var i = 0
//isInterrupted是線程屬性,interrupt()後變爲false
while (i < 5 && !Thread.currentThread().isInterrupted) {
// 一個執行計算的循環,只是爲了佔用 CPU
if (System.currentTimeMillis() >= nextPrintTime) {
println("thread: I'm sleeping ${i++} ...")
// 每秒打印消息兩次
nextPrintTime += 500L
}
}
}
thread.interrupt()
Thread.sleep(3 * 1000)
println("end")
}
結合isInterrupted進行中斷判斷,打印出:
thread: I'm sleeping 0 ... end 假設沒有結合isInterrupted進行判斷,打印出: thread: I'm sleeping 0 ...
end
thread: I'm sleeping 1 ... thread: I'm sleeping 2 ...
thread: I'm sleeping 3 ... thread: I'm sleeping 4 ...
複製代碼
父協程手動調用cancel()或者異常結束,會當即取消它的全部子協程。
下例cancel()父協程job,其neibu內部的子協程都會跟着也cancel掉。
fun cancel() = runBlocking {
val job = launch {
launch(Dispatchers.Default) {
var nextPrintTime = System.currentTimeMillis()
var i = 0
//isActive是協程擴展屬性,cancel()後變爲false
while (i < 5 && isActive) {
// 一個執行計算的循環,只是爲了佔用 CPU
if (System.currentTimeMillis() >= nextPrintTime) {
println("child launch: I'm sleeping ${i++} ...")
// 每秒打印消息兩次
nextPrintTime += 500L
}
}
}
async(Dispatchers.IO) {
var nextPrintTime = System.currentTimeMillis()
var i = 0
//isActive是協程擴展屬性,cancel()後變爲false
while (i < 5 && isActive) {
// 一個執行計算的循環,只是爲了佔用 CPU
if (System.currentTimeMillis() >= nextPrintTime) {
println("child async: I'm sleeping ${i++} ...")
// 每秒打印消息兩次
nextPrintTime += 500L
}
}
}
}
delay(1000)
job.cancel()
println("cancel parent job")
delay(10 * 1000)
}
打印出:
child launch: I'm sleeping 0 ... child async: I'm sleeping 0 ...
cancel parent job
複製代碼