协程经过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
复制代码