协程.md
- 协程
- 协程中使用suspend修饰方法,代表该方法可在协程中挂起。但并不是协程方法必须使用suspend修饰
- 协程和线程的对比:
- 线程拥有独立的栈、局部变量,给予进程的共享内存,多线程需要加锁来控制访问,而加锁会容易导致死锁,线程的调度有内核控制(时间片竞争机制)程序员无法介入,线程切换的代价较大:
1、线程对象的创建和初始化及销毁
2、线程上下文切换
3、线程状态的切换由系统内核完成
4、对变量操作需要加锁 - Coroutine协程运行在线程中,有自己的栈内存和局部变量,共享成员变量,Coroutine可以直接编辑方法,由程序员实现切换和调度,不采用时间片竞争机制,一个线程可以跑多个协程,一个时间只能执行一个协程
1、因为在一个线程中,协程的切换不涉及线程上下文的切换,不存在数据资源并发,不用加锁,执行效率高
2、协程是非阻塞的,一个协程在进入阻塞后不会阻塞线程,线程会执行其他协程
- 线程拥有独立的栈、局部变量,给予进程的共享内存,多线程需要加锁来控制访问,而加锁会容易导致死锁,线程的调度有内核控制(时间片竞争机制)程序员无法介入,线程切换的代价较大:
- 协程的使用
- GlobalScope.launch()
private fun getTest() {
Log.d("cjq", "协程初始化开始,时间: " + System.currentTimeMillis())
GlobalScope.launch(Dispatchers.Unconfined) {
Log.d("cjq","协程 ${Thread.currentThread()}")
for (i in 1..3) {
Log.d("cjq", "协程任务1打印第$i 次,时间: " + System.currentTimeMillis())
}
delay(500)
for (i in 1..3) {
Log.d("cjq", "协程任务2打印第$i 次,时间: " + System.currentTimeMillis())
}
}
Log.d("cjq","主线程 ${Thread.currentThread()}")
Thread.sleep(500)
Log.d("cjq", "主线程运行,时间: " + System.currentTimeMillis())
for (i in 1..3) {
Log.d("cjq", "主线程打印第$i 次,时间: " + System.currentTimeMillis())
}
}
D/cjq: 协程初始化开始,时间: 1626134403936
D/cjq: 主线程 Thread[main,5,main]
D/cjq: 协程 Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: 协程任务1打印第1 次,时间: 1626134404003
D/cjq: 协程任务1打印第2 次,时间: 1626134404003
D/cjq: 协程任务1打印第3 次,时间: 1626134404003
D/cjq: 主线程运行,时间: 1626134404539
D/cjq: 主线程打印第1 次,时间: 1626134404540
D/cjq: 主线程打印第2 次,时间: 1626134404540
D/cjq: 主线程打印第3 次,时间: 1626134404540
D/cjq: 协程任务2打印第1 次,时间: 1626134404552
D/cjq: 协程任务2打印第2 次,时间: 1626134404552
D/cjq: 协程任务2打印第3 次,时间: 1626134404552
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
}
launcher有3个参数和一个返回值job ,第一个参数CoroutineContext 理解为协程的上下文,可设置4种线程模式:
~ Dispatchers.Default 不设置即是Dispatcher.Default
~ Dispatcher.IO
~ Dispatcher.Main
~ Dispatcher.Umconfined 未指定线程,在当前线程
第二个参数CoroutineStart 启动模式
~ Default 默认模式,创建就启动
~ Atomic
~ Undispatched
~ Lazy 懒加载模式,需要时启动
private fun getTest() {
Log.d("cjq", "协程初始化开始,时间: " + System.currentTimeMillis())
val job :Job = GlobalScope.launch(start = CoroutineStart.LAZY) {
Log.d("cjq","协程 ${Thread.currentThread()}")
for (i in 1..3) {
Log.d("cjq", "协程任务1打印第$i 次,时间: " + System.currentTimeMillis())
}
delay(500)
for (i in 1..3) {
Log.d("cjq", "协程任务2打印第$i 次,时间: " + System.currentTimeMillis())
}
}
Log.d("cjq","主线程 ${Thread.currentThread()}")
Thread.sleep(500)
Log.d("cjq", "主线程运行,时间: " + System.currentTimeMillis())
job.start()
for (i in 1..3) {
Log.d("cjq", "主线程打印第$i 次,时间: " + System.currentTimeMillis())
}
}
D/cjq: 协程初始化开始,时间: 1626135267624
D/cjq: 主线程 Thread[main,5,main]
D/cjq: 主线程运行,时间: 1626135268190
D/cjq: 主线程打印第1 次,时间: 1626135268216
D/cjq: 主线程打印第2 次,时间: 1626135268216
D/cjq: 主线程打印第3 次,时间: 1626135268217
D/cjq: 协程 Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: 协程任务1打印第1 次,时间: 1626135268218
D/cjq: 协程任务1打印第2 次,时间: 1626135268218
D/cjq: 协程任务1打印第3 次,时间: 1626135268218
D/cjq: 协程任务2打印第1 次,时间: 1626135268760
D/cjq: 协程任务2打印第2 次,时间: 1626135268760
D/cjq: 协程任务2打印第3 次,时间: 1626135268760
返回值job 理解为协程对象本身
1、 job.start() 启动协程,除了lazy模式,其他都不需要手动启动
2、 job.join() 等待协程执行完毕
3、 job.cancel() 取消协程
4、 job.cancelAndJoin() 等待协程执行完毕后取消
- GlobalScope.async
private fun getTest() {
Log.d("cjq", "协程初始化开始,时间: " + System.currentTimeMillis())
val job :Job = GlobalScope.launch() {
Log.d("cjq","协程 ${Thread.currentThread()}")
val deferred = GlobalScope.async {
delay(1000)
Log.d("cjq","协程async ${Thread.currentThread()}")
return@async "async"
}
Log.d("cjq","协程async外 ${Thread.currentThread()}")
val result = deferred.await()
Log.d("cjq","协程async返回值 $result")
}
Log.d("cjq","主线程 ${Thread.currentThread()}")
}
D/cjq: 协程初始化开始,时间: 1626136029482
D/cjq: 主线程 Thread[main,5,main]
D/cjq: 协程 Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: 协程async外 Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: 协程async Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: 协程async返回值 async
deferred继承Job,并增加了一个方法await,async不会阻塞当前线程,但会阻塞所在协程,也就是挂起。如下比较:
private fun test2() {
GlobalScope.launch(Dispatchers.IO) {
Log.d("cjq", "launch : ${Thread.currentThread()}")
val token = GlobalScope.async {
return@async getToken()
}.await()
Log.d("cjq","token:$token")
val string = GlobalScope.async {
return@async getString()
}.await()
getBody()
}
Log.d("cjq", "out : ${Thread.currentThread()}")
}
suspend fun getToken():String{
Log.d("cjq", "gettokenstart : ${Thread.currentThread()}")
delay(300)
Log.d("cjq", "gettoken : ${Thread.currentThread()}")
return "sdf"
}
suspend fun getString():String{
Log.d("cjq", "getStringstart : ${Thread.currentThread()}")
delay(100)
Log.d("cjq", "getString : ${Thread.currentThread()}")
return "sdf"
}
fun getBody():String{
Log.d("cjq", "getBody : ${Thread.currentThread()}")
return "body"
}
D/cjq: out : Thread[main,5,main]
D/cjq: launch : Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: gettokenstart : Thread[DefaultDispatcher-worker-3,5,main]
D/cjq: gettoken : Thread[DefaultDispatcher-worker-3,5,main]
D/cjq: token:sdf
D/cjq: getStringstart : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getString : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getBody : Thread[DefaultDispatcher-worker-1,5,main]
private fun test2() {
GlobalScope.launch(Dispatchers.IO) {
Log.d("cjq", "launch : ${Thread.currentThread()}")
val token = GlobalScope.async {
return@async getToken()
}
Log.d("cjq","token:$token")
val string = GlobalScope.async {
return@async getString()
}
getBody()
}
Log.d("cjq", "out : ${Thread.currentThread()}")
}
suspend fun getToken():String{
Log.d("cjq", "gettokenstart : ${Thread.currentThread()}")
delay(300)
Log.d("cjq", "gettoken : ${Thread.currentThread()}")
return "sdf"
}
suspend fun getString():String{
Log.d("cjq", "getStringstart : ${Thread.currentThread()}")
delay(100)
Log.d("cjq", "getString : ${Thread.currentThread()}")
return "sdf"
}
fun getBody():String{
Log.d("cjq", "getBody : ${Thread.currentThread()}")
return "body"
}
D/cjq: out : Thread[main,5,main]
D/cjq: launch : Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: token:DeferredCoroutine{Active}@d6f7b86
D/cjq: gettokenstart : Thread[DefaultDispatcher-worker-3,5,main]
D/cjq: getBody : Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: getStringstart : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getString : Thread[DefaultDispatcher-worker-3,5,main]
D/cjq: gettoken : Thread[DefaultDispatcher-worker-3,5,main]
- runblocking
runblocking和launch区别在于runblocking的delay方法会阻塞当前线程,和Thread.sleep()一样
private fun test2(){
runBlocking {
Log.d("cjq", "run:${Thread.currentThread().name}")
delay(1000)
Log.d("cjq","runblocking")
}
Log.d("cjq", "test:${Thread.currentThread().name}")
Thread.sleep(2000)
Log.d("cjq","sleep")
}
D/cjq: run:main
D/cjq: runblocking
D/cjq: test:main
D/cjq: sleep
- 协程的挂起和恢复
1、协程内按顺序执行
2、协程挂起后需要等待挂起完成,且线程空闲才继续执行
3、suspend修饰的方法,挂起的是协程本身,不仅是该方法
单协程多suspend,如下:
private fun test2() {
GlobalScope.launch(Dispatchers.Main) {
Log.d("cjq", "launch : ${Thread.currentThread()}")
getToken()
getString()
getBody()
}
Log.d("cjq", "out : ${Thread.currentThread()}")
}
suspend fun getToken():String{
delay(300)
Log.d("cjq", "gettoken : ${Thread.currentThread()}")
return "sdf"
}
suspend fun getString():String{
delay(100)
Log.d("cjq", "getString : ${Thread.currentThread()}")
return "sdf"
}
fun getBody():String{
Log.d("cjq", "getBody : ${Thread.currentThread()}")
return "body"
}
D/cjq: out : Thread[main,5,main]
D/cjq: launch : Thread[main,5,main]
D/cjq: gettoken : Thread[main,5,main]
D/cjq: getString : Thread[main,5,main]
D/cjq: getBody : Thread[main,5,main]
多协程多suspend
private fun test2() {
GlobalScope.launch(Dispatchers.Main) {
Log.d("cjq", "launch : ${Thread.currentThread()}")
val token = GlobalScope.launch {
getToken()
}
val string = GlobalScope.launch {
getString()
}
getBody()
}
Log.d("cjq", "out : ${Thread.currentThread()}")
}
suspend fun getToken():String{
delay(300)
Log.d("cjq", "gettoken : ${Thread.currentThread()}")
return "sdf"
}
suspend fun getString():String{
delay(100)
Log.d("cjq", "getString : ${Thread.currentThread()}")
return "sdf"
}
fun getBody():String{
Log.d("cjq", "getBody : ${Thread.currentThread()}")
return "body"
}
D/cjq: out : Thread[main,5,main]
D/cjq: launch : Thread[main,5,main]
D/cjq: getBody : Thread[main,5,main]
D/cjq: getString : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: gettoken : Thread[DefaultDispatcher-worker-2,5,main]
多协程多suspend使用async,可以用来控制协程执行顺序
private fun test2() {
GlobalScope.launch(Dispatchers.Main) {
Log.d("cjq", "launch : ${Thread.currentThread()}")
val token = GlobalScope.async {
return@async getToken()
}.await()
val string = GlobalScope.async {
return@async getString()
}.await()
getBody()
}
Log.d("cjq", "out : ${Thread.currentThread()}")
}
suspend fun getToken():String{
delay(300)
Log.d("cjq", "gettoken : ${Thread.currentThread()}")
return "sdf"
}
suspend fun getString():String{
delay(100)
Log.d("cjq", "getString : ${Thread.currentThread()}")
return "sdf"
}
fun getBody():String{
Log.d("cjq", "getBody : ${Thread.currentThread()}")
return "body"
}
D/cjq: out : Thread[main,5,main]
D/cjq: launch : Thread[main,5,main]
D/cjq: gettoken : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getString : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getBody : Thread[main,5,main]
协程挂起后恢复所处线程:哪个线程恢复的协程,协程运行在哪个线程
private fun test2() {
GlobalScope.launch(Dispatchers.IO) {
Log.d("cjq", "launch : ${Thread.currentThread()}")
val token = GlobalScope.async {
return@async getToken()
}.await()
Log.d("cjq","token:$token")
val string = GlobalScope.async {
return@async getString()
}.await()
getBody()
}
Log.d("cjq", "out : ${Thread.currentThread()}")
}
suspend fun getToken():String{
Log.d("cjq", "gettokenstart : ${Thread.currentThread()}")
delay(300)
Log.d("cjq", "gettoken : ${Thread.currentThread()}")
return "sdf"
}
suspend fun getString():String{
Log.d("cjq", "getStringstart : ${Thread.currentThread()}")
delay(100)
Log.d("cjq", "getString : ${Thread.currentThread()}")
return "sdf"
}
fun getBody():String{
Log.d("cjq", "getBody : ${Thread.currentThread()}")
return "body"
}
D/cjq: out : Thread[main,5,main]
D/cjq: launch : Thread[DefaultDispatcher-worker-1,5,main]
D/cjq: gettokenstart : Thread[DefaultDispatcher-worker-3,5,main]
D/cjq: gettoken : Thread[DefaultDispatcher-worker-3,5,main]
D/cjq: token:sdf
D/cjq: getStringstart : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getString : Thread[DefaultDispatcher-worker-2,5,main]
D/cjq: getBody : Thread[DefaultDispatcher-worker-1,5,main]
-
suspendCancellableCoroutine和suspendCoroutine
将回调函数转换为协程,suspendCancellableCoroutine返回CancellableContinuation,用resume和resumeWithException 处理回调和抛出异常,和suspendCoroutine的区别在于suspendCancellableCoroutine可以通过cancel()方法手动取消协程执行 -
lifecycleScope、 viewmodelScope
引入方式:
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'//lifecycleScope
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'//viewModelScope
class MainFragment : Fragment(){
val model:RvViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch { }
}
}
class RvViewModel : ViewModel() {
fun getData() {
viewModelScope.launch(Dispatchers.IO) {
}
}
}
1、GlobalScope的生命周期是process级别的,即使activity或fragment销毁,协程仍在执行
2、lifecycleScope的作用域CoroutineScope绑定到lifecycleowner的生命周期,取消此作用域,协程销毁,lifecycleowner生命周期可以绑定到activity中,所以lifecycleScopr间接绑定到Activity和Fragment的生命周期。只能用于activity或fragment
3、viewmodelScope的作用域CoroutineScope的作用域绑定到viewmodel,viewmodel销毁,作用域清除,只能用于viewmodel
- kotlin.runcathing
代码块内会进行try catch处理,实现了onSuccess和onFailure方法
private fun initTest() {
GlobalScope.launch {
kotlin.runCatching {
var j: Int = updateUser()
Log.d("cjq-1", "$j")
return@runCatching j
}.onSuccess {
Log.d("cjq-2", "$it")
}.onFailure {
Log.d("cjq-3", "${it.message}")
}
}
}
private fun updateUser(): Int {
// return 2
throw Exception("sdf")
}
D/cjq-3: sdf
private fun initTest() {
GlobalScope.launch {
kotlin.runCatching {
var j: Int = updateUser()
Log.d("cjq-1", "$j")
return@runCatching j
}.onSuccess {
Log.d("cjq-2", "$it")
}.onFailure {
Log.d("cjq-3", "${it.message}")
}
}
}
private fun updateUser(): Int {
return 2
// throw Exception("sdf")
}
D/cjq-1: 2
D/cjq-2: 2
- AtomicBoolean 原子变量,在值变化的时候不允许在之间插入,保持操作的原子性
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
public final boolean compareAndSet(boolean expect, boolean update) {
return U.compareAndSwapInt(this, VALUE,
(expect ? 1 : 0),
(update ? 1 : 0));
}
compareAndSet()方法:
1、比较mPenging值和expect值是否相等,相等则执行方法
2、将mPending赋值为update
以上的方法则为只有在setValue()方法执行后才会执行observer.onChanged(),并执行线程同步操作