栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Kotlin学习笔记21 协程part1 基本概念

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Kotlin学习笔记21 协程part1 基本概念

参考链接

示例来自bilibili Kotlin语言深入解析 张龙老师的视频

本节先介绍协程的相关概念 概念可能枯燥,我们先要了解协程中的相关概念 然后结合代码理解这些概念 加深印象

协程的定义

协程通过将复杂性放入库中来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库为我们解决异步性

协程库可以将用户代码地相关部分包装为回调、订阅相关事件(listener)、在不同地线程(甚至不同的机器)上调度,而代码如同顺序执行一样简单

协程的描述

协程就像是轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大

而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎没有代价,协程由开发者控制。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意多个协程

总之:协程可以简化异步编程,可以顺序表达程序,协程也提供了一种避免阻塞线程并用更廉价更可控的操作代替线程阻塞的方法--协程挂起

1.协程的几个概念

CoroutingScope

CoroutingScope可以理解为协程本身 包含了CoroutingContext

CoroutingContext

CoroutingContext 协程上下文,是一些元素的集合,主要包括Job和CoroutingDispatcher元素,可以代表一个协程的场景

EmptyCoroutingContext

EmptyCoroutingContext表示一个空的协程上下文

CoroutingDispatcher

CoroutingDispatcher,协程调度器,决定协程所在的线程或线程池。他可以指定协程运行于特定的一个线程、一个线程池或者不指定任何线程(这样的协程运行于当前线程)

coroutings-core中CoroutingDispatcher有四种标准实现:

Dispatchers.Default

Dispatchers.IO:运行在IO线程

Dispatchers.Main:运行在主线程

Dispatchers.Unconfined:不指定线程

launch函数定义如果不指定CoroutingDispatcher或者没有其他的ContinuationInterceptor,默认的协程调度器就是Dispatchers.Default,Default是一个协程调度器,其指定的线程为共有的线程池,线程数量至少为2,最大数目于CPU数相同

Job & Defferred

Job,任务,封装了协程中需要执行的代码逻辑。Job可以取消并且有简单的生命周期,他有三种状态

 

Job完成时没有返回值,如果需要返回值的话,应该使用Defferred,他是Job的子类

public interface Defferred:Job

Coroutine builders

CoroutineScope.launch函数属于协程构建器Coroutine builders,Kotlin中还有其他几种协程构建器,负责创建协程

CoroutineScope.launch{}

CoroutineScope.launch{} 是最常用的Coroutine builders协程构建器,不阻塞当前线程,在后台创建一个新协程,也可以指定协程调度器

runBlocking{}

runBlocking{}是创建一个新的协程同时阻塞当前线程,直到协程结束。这个不应该在协程中使用,起作用主要是为main函数和测试设计的

withContext{}

withContext{}不会创建新的协程,在指定协程上运行挂起代码块,并挂起该协程直到代码块运行完毕

async{}

CoroutineScope.async{}可以实现与launch builder一样的效果,在后台创建一个新的协程,唯一的区别是他又返回值,因为CoroutineScope.async{}返回的是Deferred类型

注意:开始写协程的demo前 需要自行引入Kotlin协程的依赖 Kotlin考虑到有的项目可能不需要使用协程,普通的Kotlin依赖和协程的依赖是分开的,因此我们需要单独引入协程相关的依赖

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"

1.3.9是当前我编写代码时的最新协程版本

完整module的build.gradle如下:

plugins {
    id 'java-library'
    id 'kotlin'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7
}
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-reflect:1.5.20"
    // 引入协程依赖
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
}
2 CoroutineScope.launch使用
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
// 协程的第一个示例
// CoroutineScope.launch{}
// 协程结合线程
fun main() {
    
    GlobalScope.launch{// 在后台启动一个新的协程并继续
        delay(1000)// 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("Kotlin coroutine")// 在延迟后打印输出
    }

    println("hello")// 协程已在等待时主线程还在继续
    Thread.sleep(2000)// 阻塞主线程 2 秒钟来保证 JVM 存活
    println("world")// 最后输出
}



class HelloKotlin1 {
}
3.CoroutineScope.launch{} 使用线程的等价写法
import kotlin.concurrent.thread


fun main() {

    // thread是Kotlin的一个方法 接受若干参数 最后一个参数是一个lambda表达式 他是thread的执行体
    // thread创建的线程默认自动执行
    thread {
        Thread.sleep(1000)// 子线程不会阻塞主线程 计时1s
        println("Kotlin coroutine")
    }

    println("hello")// 立即输出
    Thread.sleep(2000)// 阻塞主线程2s
    println("world")// 最后输出
}



public fun thread(
    start: Boolean = true,
    isDaemon: Boolean = false,
    contextClassLoader: ClassLoader? = null,
    name: String? = null,
    priority: Int = -1,
    block: () -> Unit
): Thread {
    val thread = object : Thread() {
        public override fun run() {
            block()
        }
    }
    if (isDaemon)
        thread.isDaemon = true
    if (priority > 0)
        thread.priority = priority
    if (name != null)
        thread.name = name
    if (contextClassLoader != null)
        thread.contextClassLoader = contextClassLoader
    if (start)
        thread.start()
    return thread
}

class HelloKotlin2 {
}
4.runBlocking
import kotlinx.coroutines.*


fun main() {
    GlobalScope.launch {//GlobalScope.launch创建协程 并且不阻塞当前线程 协程计时1s
        delay(1000)
        println("Kotlin coroutine")
    }

    println("hello")

    runBlocking {
        delay(2000)
    }
    println("world")
}





class HelloKotlin3 {

}
5.CoroutineScope CoroutineScope.launch CoroutineScope.launch启动方式等
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineStart.*
import kotlinx.coroutines.launch
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext



fun main() {
    GlobalScope.launch{

    }
}

// CoroutineScope详解
// 作用:为一个新的协程定义一个作用域 以及嵌套协程的规则
// 以下摘自Kotlin源码1.3.9
val CoroutineScopeExplain = """
kotlinx.coroutines CoroutineScope.kt 
public interface CoroutineScope
为一个新的协程定义一个作用域。每一个协程构建器(例如launch async等)都是CoroutineScope的一个扩展,并且继承了它的coroutineContext来自动传播CoroutineScope的上下文元素和可取消的特性
Defines a scope for new coroutines. Every coroutine builder (like launch, async, etc) is an extension on CoroutineScope and inherits its coroutineContext to automatically propagate all its elements and cancellation.
获取独立的scope的最佳方式是通过CoroutineScope() 以及 MainScope()等工厂方法。额外的上下文元素可以使用加运算符append到scope
The best ways to obtain a standalone instance of the scope are CoroutineScope() and MainScope() factory functions. Additional context elements can be appended to the scope using the plus operator.
Convention for structured concurrency
手动实现CoroutineScope接口是不推荐的,更推荐使用委托。习惯上,scope的上下文应该包含一个job的实例,以确保强制结构化并发(以便通过取消传播来强制执行结构化并发的规则。)
Manual implementation of this interface is not recommended, implementation by delegation should be preferred instead. By convention, the context of a scope should contain an instance of a job to enforce the discipline of structured concurrency with propagation of cancellation.
每一个协程构建器(例如launch, async等等)以及每一个作用域方法(例如coroutineScope, withContext等等)都提供了自己的scope和它自己的Job实例到它的运行的内部代码块中。 根据约定,他们都等待他们块内的所有协程完成,然后再完成自己(协程嵌套协程 外部的协程要等到内部的协程执行完毕 才能结束自身)从而强制执行结构化并发
Every coroutine builder (like launch, async, etc) and every scoping function (like coroutineScope, withContext, etc) provides its own scope with its own Job instance into the inner block of code it runs. By convention, they all wait for all the coroutines inside their block to complete before completing themselves, thus enforcing the structured concurrency. See Job documentation for more details.
Android usage
Android has first-party support for coroutine scope in all entities with the lifecycle. See the corresponding documentation .
Custom usage
CoroutineScope应当在一个实体中被实现或声明为一个属性 并且有一个定义良好的生命周期 他们负责启动子的协程(这种实体在Android中有一个好的例子 就是Activity)
CoroutineScope should be implemented or declared as a property on entities with a well-defined lifecycle that are responsible for launching children coroutines, for example:
class MyUIClass {
    val scope = MainScope() // the scope of MyUIClass

    fun destroy() { // destroys an instance of MyUIClass
        scope.cancel() // cancels all coroutines launched in this scope
        // ... do the rest of cleanup here ...
    }

    
        fun showSomeData() = scope.launch { // launched in the main thread
           // ... here we can use suspending functions or coroutine builders with other dispatchers
           draw(data) // draw in the main thread
        }
    }
""".trimIndent()


val CoroutineScopeDotLaunch = """
    可以看到CoroutineScope.launch接受三个参数 前两个参数有默认值 返回一个Job对象
    suspend关键字用来定义一个挂起函数 该关键字只能用在协程内部或者一个suspend函数中
    kotlinx.coroutines Builders.common.kt public fun CoroutineScope.launch(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() → Unit
    ): Job
 * 启动一个新的协程而不会阻塞当前线程 并且以Job的形式返回当前协程的引用
 * Launches a new coroutine without blocking the current thread and returns a reference to the coroutine as a [Job].
 * 当生成的job被取消时 当前协程也会取消
 * The coroutine is cancelled when the resulting job is [cancelled][Job.cancel].
 *
 * 协程上下文继承自CoroutineScope。额外的上下文元素可以通过context参数指定。
 * The coroutine context is inherited from a [CoroutineScope]. Additional context elements can be specified with [context] argument.
 * 如果上下文没有指定dispatcher调度器/分发器 或者任何其他ContinuationInterceptor() 那么默认会使用Dispatchers.Default
 * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used.
 * 父Job也继承自CoroutineScope 但是它也可以被一个对应的上下文元素覆盖掉
 * The parent job is inherited from a [CoroutineScope] as well, but it can also be overridden
 * with a corresponding [context] element.
 *
 * 默认情况下 协程会立刻调度执行
 * By default, the coroutine is immediately scheduled for execution.
 * 其他开启选项可以通过start参数指定
 * Other start options can be specified via `start` parameter. See [CoroutineStart] for details.
 * 一个可选的 start参数是CoroutineStart.LAZY以延迟启动协程 在这种情况下 协程Job被创建为 new state
 * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,
 * 它可以通过调用Job.start方法被显示地启动 也可以在第一次调用Job.join时被隐式启动
 * the coroutine [Job] is created in _new_ state. It can be explicitly started with [start][Job.start] function
 * and will be started implicitly on the first invocation of [join][Job.join].
 *
 * 当前协程地未捕获异常默认会取消当前上下文地父job
 * Uncaught exceptions in this coroutine cancel the parent job in the context by default
 * (除非CoroutineExceptionHandler被指定) 这意味着当launch被其他协程地上下文调用时 任何未捕获异常都会导致父协程取消
 * (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used with
 * the context of another coroutine, then any uncaught exception leads to the cancellation of the parent coroutine.
 *
 * See [newCoroutineContext] for a description of debugging facilities that are available for a newly created coroutine.
 *
 * @param context additional to [CoroutineScope.coroutineContext] context of the coroutine.
 * @param start coroutine start option. The default value is [CoroutineStart.DEFAULT].
 * @param block the coroutine code which will be invoked in the context of the provided scope.

""".trimIndent()


val launchStart ="""
    启动方式时枚举类型CoroutineStart的实例
 * Defines start options for coroutines builders.
 * 定义了协程构建器的启动方式 它被用在CoroutineScope.launch CoroutineScope.async以及其他构建器方法的start参数中
 * It is used in `start` parameter of [launch][CoroutineScope.launch], [async][CoroutineScope.async], and other coroutine builder functions.
 *
 * The summary of coroutine start options is:
 *                立刻根据上下文调度协程执行
 * * [DEFAULT] -- immediately schedules coroutine for execution according to its context;
                延时启动协程 只有需要的时候才启动 
 * * [LAZY] -- starts coroutine lazily, only when it is needed;
                 根据上下为自动(以不可取消的方式)调度协程
 * * [ATOMIC] -- atomically (in a non-cancellable way) schedules coroutine for execution according to its context;
                 立即执行协程直到遇到它在当前线程的第一个挂起点
 * * [UNDISPATCHED] -- immediately executes coroutine until its first suspension point _in the current thread_.    
""".trimIndent()


class HelloKotlin3_2 {
}
6.delay
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking


fun main() = runBlocking {// 开始执行主协程
    GlobalScope.launch {// 在后台用新的协程构建器 启动一个新的协程并继续
        delay(1000)
        println("Kotlin coroutine")
    }
    println("hello")// 主协程在这里会立即执行
    delay(2000)// 延迟 2 秒来保证 JVM 存活
    println("world")// 最后输出
}



class HelloKotlin4 {
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/287411.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号