浅谈协程
什么是协程
进程是系统进行资源分配和保护的基本单位,线程是处理器调度和分派的基本单位
- 进程具有一个独立的执行环境。 进程是程序在计算机上的一次执行活动 。通常情况下,进程拥有一个完整的、私有的基本运行资源集合。特别地,每个进程都有自己的内存空间。
- 线程有时也被称为轻量级的进程。进程和线程都提供了一个执行环境,但创建一个新的线程比创建一个新的进程需要的资源要少。线程是在进程中存在的,每个进程最少有一个线程。线程又如下特点:
- 线程共享进程的资源,包括内存和打开的文件。线程之间的通信不用进行系统调用,更节约时间
- 线程更轻量,线程实体包括程序,数据和TCB
- 线程是调度的基本单位
协程,是英语翻译过来的(Coroutine),也叫纤程,是协作的程序。
- 协程,不让OS通过竞争的方式决定调用哪些线程,而是由用户自己决定如何去执行逻辑。
- 它可以让我们用逻辑流的顺序去写控制流,而且还不会导致操作系统级的线程阻塞。因为协程是由应用程序决定的,所以任务切换的上下文也会交给了用户态来保存。
- 减少上下文切换导致的资源消耗
实现
线程的实现方式主要有三种:分别是使用内核线程实现、使用用户线程实现以及使用用户线程加轻量级进程混合实现。
内核线程(Kernel-Level Thread,KLT)
- 就是直接由操作系统内核(Kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上,并向应用程序提供API接口来管理线程。
- 由于是基于内核线程实现的,所以各种线程操作,如创建、析构及同步,都需要进行系统调用。而系统调用的代价相对较高,需要在用户态(User Mode)和内核态(Kernel Mode)中来回切换。其次,每个轻量级进程都需要有一个内核线程的支持,因此轻量级进程要消耗一定的内核资源(如内核线程的栈空间),因此一个系统支持轻量级进程的数量是有限的。
用户线程(Kernel-Level Thread,KLT)
- 用户空间建立线程库,通过运行时系统(Run-time System)来完成线程的管理,因为这种线程的实现是在用户空间的,所以操作系统的内核并不知道线程的存在,所以内核管理的还是进程,所以这种线程的切换不需要内核操作。
- 这种线程实现方式的优点是线程切换快,并且可以运行在任何操作系统之上,只需要实现线程库就行了。但是缺点也比较明显,就是所有线程的操作都需要用户程序自己处理,并且因为大多数系统调用都是阻塞的,所以一旦一个进程阻塞了,那么进程中的所有线程也会被阻塞。还有就是多处理器系统中如何将线程映射到其他处理器上也是一个比较大的问题。
混合实现线程
实现原理其实是JDK不再是每一个线程都一对一的对应一个操作系统的线程了,而是会将多个虚拟线程映射到少量操作系统线程中,通过有效的调度来避免那些上下文切换。
虚拟线程总是守护线程。setDaemon (false)方法不能将虚拟线程更改为非守护线程。所以,需要注意的是,当所有启动的非守护线程都终止时,JVM将终止。这意味着JVM不会等待虚拟线程完成后才退出。
其次,即使使用setPriority()方法,虚拟线程始终具有normal的优先级,且不能更改优先级。在虚拟线程上调用此方法没有效果。
Thread.Builder virtualBuilder = Thread.ofVirtual().name("虚拟线程"); // 不建议和线程池一起使用,多此一举 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10000).forEach(i -> { executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return i; }); }); }
浅谈协程
http://lzhnet.top/2023/09/26/浅谈协程/