### 什么是协程

    子程序或者称为函数,在所有的语言中都是层级调用,如:A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。所以子程序是 通过栈来实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。

    而协程看上去是子程序,执行的过程中,在 子程序中可中断,去执行其他的子程序,在适当的时候可以回来接着执行。

    官方的定义:协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。

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

### 协程使用场景

    协程是一种轻量级的方便操作异步代码的语法糖,而它本身不提供异步能力。

    Java 中程序 出现异常,通常会在回调中加入一个 onError 传递异常信息。

interface Callback{ 
            fun onError(e: CertainException)   
            fun onSuccess(data: Data) 
        }

     在协程的只支持下,我们只要按照同步代码的异常捕获方式进行捕获就可以

async{ 
            try{ 
                val bitmap = await{ loadImage(url) } 
            }catch(e: CertainException){ 
                ... 
            }
        }

    协程更加的轻量级,因为协程只是 一块内存,而线程是对应 操作系统的内核线程。

### 线程和协程区别

    1.协程是编译器级别的,线程是系统级别的,协程的切换是由程序来控制的,线程的切换是由操作系统来控制的。

    2.协程是协作式的,线程式抢占式的,协程式由程序来控制怎么执行切换;而线程是由系统来决定线程之间的切换。

    3.一个线程可以包含多个协程。

    4.Java中,多线程可以充分利用多核cpu,协程是在一个线程中执行。

    5.协程适合io密集型的程序,多线程适合计算密集型的程序(适用于多核cpu情况),当程序中大部分是 文件读写和网络请求操作,这时首选 协程。协程的执行效率较高,子程序的切换时由程序自身控制,因此没有线程切换的开销。

    6.使用协程可以顺序调用异步代码,避免回调地狱。

### 协程用法

//这段代码就是启动一个协程,并启动,延迟1秒后打印world,就从这个launch方法进行切入
    GlobalScope.launch { 
        delay(1000L) //  延迟(挂起)1000毫秒,注意这不会阻塞线程
        println("World!") 
    }

    怎么选择启动模式,协程默认是 Deafault,就是创建完成立即启动,当然我们也可以设置位 LAZY,来安排什么时候启动。

val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
        delay(1000L)
        LogUtils.i("AndroidActivity"+"设置启动模式")
    }
    LogUtils.i("AndroidActivity"+"hello world")
    job.start()

### 协程之间的切换

//这段代码先打印出next,然后延迟1秒钟后打印出now,像是android里handler的post和postDelay方法。
    GlobalScope.launch(Dispatchers.Default){
        LogUtils.i("AndroidActivity"+"协程间是如何切换的")
        LogUtils.i("AndroidActivity---${Thread.currentThread().name}")
        launch {
            delay(1000)
            LogUtils.i("AndroidActivity"+"now")
        }
        LogUtils.i("AndroidActivity"+"next")
    }

### 协程如绑定在指定的线程中

    首先创建一个 CoroutineScope (协同程序范围),所有协程都会运行在 CoroutineScope,在CoroutineScope中传入参数 Dispatchers.Main,这是一个协程调度器,规定了协程在执行的时候,使用一个或多个线程,协程调度器可以将协程执行局限在指定的线程中,调度它运行在线程池中或让它不受限的运行。

    调用 launch启动一个协程,launch方法会返回一个job,调用 cancel 方法可以取消协程的运行。

    调用 async 又启动一个协程,同时调用 Dispatchers.Default这个协程调度器,使协程在执行时使用一个DefaultDispatcher-worker-1线程,

    为什么使用async 而没有使用launch?

        因为async会返回一个Deferred对象,调用其await方法可以阻塞执行流等到协程执行完毕返回结果,这样可以得到一个返回值,在这个async创建的协程里使用了使用了suspend方法先休眠2秒钟,然后返回一个字符串,注意这里这个delay也是suspend方法,一个suspend方法只能在协程或者suspend方法里调用。

private fun coroutineScope() {
        val uiScope = CoroutineScope(Dispatchers.Main)
        uiScope.launch {
            LogUtils.i("AndroidActivity"+"模拟发送请求")
            val deffer = async(Dispatchers.Default) {
                getResult()
            }
            val result = deffer.await()
            LogUtils.i("AndroidActivity---获取 $result")
        }
    }

    private suspend fun getResult(): String {
        delay(2000L)
        LogUtils.i("AndroidActivity"+"模拟请求时间")
        return "result"
    }
    //打印日志如下
    AndroidActivity模拟发送请求
    2秒后
    AndroidActivity模拟请求时间
    AndroidActivity---获取 result

### 协程Job类

    调用launch,就启动了一个协程,launch方法会返回一个job, job.cancel()取消一个协程

fun main() {
        val job = GlobalScope.launch {
            delay(1000L)
            println("World!")
        }
        job.cancel()
        println("Hello,")
    
    }

    join()等待协程执行完毕:作用很像Thread.join()函数,join()后面的代码会等到协程结束再执行,

fun main() = runBlocking {
        val job = GlobalScope.launch {
            delay(1000L)
            println("World!")
            delay(1000L)
        }
        println("Hello,")
        job.join() 
        println("Good!")
    }
    
    //依次打印
    Hello,
    World!
    Good!

    job.cancelAndJoin()取消并且加入一个协程,等待协程执行完毕然后再取消 这是一个 Job 的扩展函数,它结合了 cancel 和 join的调用

public suspend fun Job.cancelAndJoin() {
        cancel()
        return join()
    }