OKHTTP 线程 协程

1、阻塞和挂起的区别:

  • 线程的阻塞:

阻塞状态的线程的特点是:该线程放弃CPU的使用,暂停运行,只有等到导致阻塞的原因消除之后才回复运行。在阻塞过程中,被其他的线程中断,该线程也会退出阻塞状态,同时抛出InterruptedException。
正在执行的进程由于发生(如I/O请求、申请资源失败等)暂时无法继续执行。此时引起进程调度,OS把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态,一般将这种状态称为阻塞状态。

  • 进程的挂起

机器的资源是有限的,在资源不足的情况下,其中有的进程被暂时调离出内存,当条件允许的时候,会被操作系统再次调回内存,重新进入等待被执行的状态即就绪态

  • 共同点:
  1. 都暂停执行
  2. 都释放CPU,即两个过程都会涉及上下文切换
  • 不同点:

1、挂起是主动的,阻塞是被动的
1. 对系统资源占用不同:虽然都释放了CPU,但阻塞的进程仍处于内存中,而挂起的进程通过“对换”技术被换出到外存(磁盘)中。
2. 发生时机不同:阻塞一般在进程等待资源(IO资源、信号量等)时发生;而挂起是由于用户和系统的需要,例如,终端用户需要暂停程序研究其执行情况或对其进行修改、OS为了提高内存利用率需要将暂时不能运行的进程(处于就绪或阻塞队列的进程)调出到磁盘
3. 恢复时机不同:阻塞要在等待的资源得到满足(例如获得了锁)后,才会进入就绪状态,等待被调度而执行;被挂起的进程由将其挂起的对象(如用户、系统)在时机符合时(调试结束、被调度进程选中需要重新执行)将其主动激活

  • 挂起的原因可能是如下几种情况:

(1)通过调用sleep()方法使线程进入休眠状态,线程在指定时间内不会运行。
(2)通过调用join()方法使线程挂起,使自己等待另一个线程的结果,直到另一个线程执行完毕为止。
(3)通过调用wait()方法使线程挂起,直到线程得到了notify()和notifyAll()消息,线程才会进入“可执行”状态。
(4)使用suspend挂起线程后,可以通过resume方法唤醒线程。

  • 调用sleep()、yield()、suspend()的时候,如果释放对象(如果持有)
  • 调用wait()的时候释放当前对象
  • wait()方法表示,放弃当前对资源的占有权,一直等到有线程通知,才会运行后面的代码。
  • notify()方法表示,当前的线程已经放弃对资源的占有,通知等待的线程来获得对资源的占有权,但是只有一个线程能够从wait状态中恢复,然后继续运行wait()后面的语句。
  • notifyAll()方法表示,当前的线程已经放弃对资源的占有,通知所有的等待线程从wait()方法后的语句开始运行。

2、需要子线程的地方:

IO操作:网络操作、数据库的操作、自己写一个相册,加载图片数据时,用子线程去做
逻辑处理:计算量比较大的

3、在子线程执行任务时,当子线程任务执行结束,调用接口返回主线程,在主线程实现的接口方法中打印当前线程时,还是子线程,因为是子线程调用的这个接口方法。所以不能在接口方法的实现中操作UI控件。

  • 解决方法:可以在接口方法的实现中操作UI之前切换到主线程: runONUIThread{ 操作 }

4、线程切换的问题:存在线程嵌套线程、接口嵌套接口的问题(接口二的调用前提是接口一被执行)。此时对线程的操控太过麻烦

协程:

  • Coroutine的个数没有限制
  • 子线程执行时,当遇到Coroutine,会将这个Coroutine挂起(suspend),让这个Coroutine自己去执行,当其执行完毕,再将这个Coroutine恢复 (resume)到子线程
  • delay:延迟一个协程,不会阻塞当前线程,当延迟事件结束后,被延迟协程会自动resume。但是它必须使用在一个协程或是一个被suspend修饰的函数中
  • 任何一个协程都要有自己的CortoutineScope,同时在这个协程域中可以创建无数个子协程
  • suspend修饰的挂起函数只能在另外一个挂起函数或是一个CortoutineScope中执行(因为CortoutineScope支持挂起)
  • 在java中,普通的线程操作,主线程的任务执行完成了,但是子线程还没有执行结束时,主线程会等待子线程

6、使用协程:

  • 1、导入依赖:

1、kotlin:
implementation 'org.jetbrains,. katlinx:katlinx-coroutines-core:1.3.9'
2、android:
implementation 'org.jetbrains.kotlinx:katlinx-coroutines-core:1
implementation 'org.jetbrains.kotlinx : katlinx-coroutines android:1.3.9'

  • 2、创建CortoutineScope:
  • launch:创建一个独立的CoroutineScope,同步的,一般不返回数据
    launch{ 操作 }
  • async:异步,可以返回数据
  • runBlocking:一般只有测试使用这个方法,在当前线程创建协程域,但是它的执行会阻塞当前的线程
    runBlocking{ 操作 }
  • GlobalScope:创建一个全局的scope,但是这个尽量不要使用,因为作用域为整个程序的lifecycle。使用它时,会创建一个新的线程,在其中运行。当主线程执行完毕,它不会等待GlobalScope执行完毕
    GlobalScope.launch{ 操作 }
  • 3、协程的执行:当协程是在主线程上时,它被挂起又resume后,回到的还是主线程;但是当协程是在子线程时,协程被挂起的时候,可能它所属的线程就被回收了,当协程resume时,系统会另外给这个协程分配一个线程。
  • 4、coroutineScope:使用async和launch时都是创建一个新的scope;
    CoroutineContext:async和launch和parent scope在同一个context(线程)中,但是GlobalScope执行时会创建一个新的线程
  • 5、Android一般由三类线程:main线程、IO线程、default线程(适合执行需要大量CPU的操作,例如大的计算操作)
    在launch中可以指定它执行的线程:launch(Dispatchers.线程){ 操作 };例如:
    launch(Dispatchers.IO) { 操作 } 。async也一样可以指定线程
  • 6、await() 方法可以同步协程,并返回上一个协程的数据
  • 7、协程可以自动完成线程的切换,不需要用户的干预
  • 8、withContext:Dispatchers可以指定线程,withContext可以切换线程,它有返回值(可以作为回调),默认是它作用域的最后一句话。withContext是阻塞式的,它在执行时会阻塞所在线程。用法:
launch(Dispatchers.IO){ 操作 }
withContext(Dispatchers.IO){ 操作 }
  • 10、Coroutine结合OKHTTP的用法:
    viewModelScope继承于CoroutineScope


协程的定义:

  • 官方描述:协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器)上调度执行,而代码则保持如同顺序执行一样简单。

你可能感兴趣的:(OKHTTP 线程 协程)