你居然问我单线程能不能实现并发?你说能不能!

单线程能不能实现并发

乍一看这个问题,我第一时间的想法是并发这个词跟单线程好像并不沾边?在我的刻板印象里好像只有多线程和并发有关

但我回想起我的计算机基础,我很确定的是,单线程指的是只有一个执行线程,在任意时刻只能执行一个任务。

因此在传统意义上,单线程它就是没有办法实现真正的并发的,但是单线程是可以实现类似并发的效果的

我称这种效果为 "伪并发" ,给大家列举一下实现 "伪并发" 的一些方式

  1. 时间分片:在单线程中在每个单位时间里,通过切换不同任务的执行顺序,模拟多个任务同时执行的效果 通过任务调度器,可以让不同任务交替执行,从而实现 "伪并发"。这里可以联系单核CPU和多核CPU的概念

  2. 事件循环 :使用事件驱动模型,在单线程中处理多个事件。通过事件循环机制,程序可以同时处理多个事件 看上去是同时发生的,其实是一种 "伪并发"

  3. 协程:协程可以在单线程中实现并发执行,通过协程的切换机制,程序可以在不同的执行点之间快速切换,实现 "伪并发"

  4. 异步编程:异步编程是一种编程范式,它允许程序在执行时不会因为等待某个操作(如I/O操作或网络请求)的结果而阻塞主线程的其他任务。这种编程模式使得应用程序能够更加高效地利用系统资源。(这里可能不好理解,可以往下看)

那什么是协程

老实说,第一次接触协程,一脸懵 ,查阅了很多资料也根本不知道这协程到底怎么回事,经过单线程实现 "伪并发" 的理论

对协程总算是有了一点理解,本文的描述可能不怎么严谨,主要是让大家对协程有一点认识

在我尝试对协程进行理解的时候,我脑子里面就构筑了这个模型,我就在想,那不是有多线程吗?为什么还需要在单线程里面

再搞个协程呢?对于这个问题就想了想多线程有什么问题呢?为什么会需要在单线程里面开协程这个概念呢?

直接给大家上结论:多线程的开销最主要是来源于线程阻塞唤醒调度

相信大家对减少上下文切换 这个概念并不陌生,翻译过来就是减少线程阻塞唤醒的调度次数


好,我们正式进入协程的概念的理解,允许在单个线程内执行多个并发的、非抢占式的控制流。协程的特点和优势在于,它们能够在运行时主动挂起自己的执行,并将控制权返回给调用者或其他协程。当条件满足或被其他协程唤醒时,可以从上次挂起的地方恢复执行。

其实到这里大家可能对为什么要使用协程有了一点苗头了,大胆发文,协程可以减少线程阻塞唤醒调度的开销,切换成本低

就是这样,那么为什么协程与传统线程相比,切换成本更低了?

因为协程的上下文切换完全在用户空间进行,不需要像操作系统调度线程那样涉及内核态的切换。此外,协程的调度策略由程序员自行控制,因此可以更灵活地管理资源并实现协同式多任务处理。

什么是异步编程

异步编程是一种处理非阻塞IO和并发任务的编程方式,简单来说就是开启另一个服务或者线程去处理任务

  1. 使用线程和Runnable/Callable接口:Java中最基本的异步编程方式是通过创建线程来实现。可以通过实现Runnable接口或Callable接口 ,并将其传递给Thread类来创建线程。这样可以在单独的线程中执行任务,从而实现异步操作。

  2. 使用Future和Callable:Java中的Futrue接口和Callable接口可以用于异步执行任务并获取任务的结果。Callable接口类似于Runnable接口,但可以返回任务的执行接口。Future接口可以用来获取异步执行任务的执行状态和结果。

  3. 使用FutureTask(不推荐)或CompleableFuture(推荐):Java8引入了CompleableFuture类,它提供了更强大和更灵活的异步编程功能。Compleable可以用于组合多个异步任务,处理任务的结果和异常,以及实现复杂的异步操作流程。

  4. 引用消息队列:可以通过生产消息发送到消息队列,通过消息队列进行解耦,实现业务逻辑的异步操作

123点这样开启额外的线程去处理,还是多线程那个意思,这里只是举个异步编程的例子,消息队列这就没有用到多线程了

什么是事件循环

相信学过JavaScript和Node的人对事件循环那肯定是熟悉的很

Event Loop 即事件循环, 是JavaScript或Node为解决单线程代码执行不阻塞主进程一种机制,也就是我们所说的异步原理。

事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。


  • 整个运行机制是这样的

    1. 整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为两部分:"同步任务"、"异步任务"

    2. 同步任务会直接进入主线程依次执行

    3. 异步任务 会再分为宏任务 (进入宏任务队列) 和 微任务(进入微任务队列)

    4. 当主线程内的任务执行完毕 (主线程为空时),会检查微任务的任务队列,如果有任务,就进入主线程全部执行,如果没有就从宏任务队列读取下一个宏任务执行

    5. 每执行完一个宏任务就清空一次微任务队列,此过程会不断重复,这就是Event Loop

相关推荐
少年维持着烦恼.几秒前
第八章习题
前端·css·html
我是哈哈hh3 分钟前
HTML5和CSS3的进阶_HTML5和CSS3的新增特性
开发语言·前端·css·html·css3·html5·web
田本初21 分钟前
如何修改npm包
前端·npm·node.js
明辉光焱42 分钟前
[Electron]总结:如何创建Electron+Element Plus的项目
前端·javascript·electron
java小吕布1 小时前
Java中的排序算法:探索与比较
java·后端·算法·排序算法
牧码岛1 小时前
Web前端之汉字排序、sort与localeCompare的介绍、编码顺序与字典顺序的区别
前端·javascript·web·web前端
开心工作室_kaic1 小时前
ssm111基于MVC的舞蹈网站的设计与实现+vue(论文+源码)_kaic
前端·vue.js·mvc
Goboy1 小时前
工欲善其事,必先利其器;小白入门Hadoop必备过程
后端·程序员
晨曦_子画2 小时前
用于在 .NET 中构建 Web API 的 FastEndpoints 入门
前端·.net