【JavaEE】多线程(一)

news2024/11/27 5:35:41

多线程(一)

文章目录

  • 多线程(一)
    • 进程
      • 操作系统
      • 进程
      • PCB属性
        • 进程的状态
        • 进程的优先级
        • 进程的上下文
          • CPU寄存器
      • 进程的记账信息
      • 虚拟地址空间
    • 线程
      • 线程与进程的区别
    • Java进行多线程编程

在了解多线程之前,我们先聊聊进程

进程

而了解进程前,我们还需聊聊操作系统~

操作系统

简单来理解就是搞管理的软件。

  1. 对下:管理好各种硬件设备
  2. 对上:要给应用程序通过稳定的运行环境

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

操作系统的“内核态” 是操作系统里面最核心的功能模块, 硬件的驱动程序都是在系统内核中执行的~ 内核需要给很多应用程序提供支持~

一个程序在运行中,可能是用户态在工作,也可能是内核态在工作~

一个操作系统 = 内核 + 配套的应用程序


而操作系统的内核涉及很多关键性概念,进程只是其中一个,这里作为引入,所以接下来详细聊聊进程~

进程

每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运行,所有的硬件资源都被这个程序在使用。这种假象是通过抽象了一个进程的概念来完成的,进程可以说是计算机科学中最重要和最成功的概念之一。

进程process/task,通俗来看,一个已经跑起来的程序,就是进程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以上都是在执行的进程,对于windows这个系统来说,一开机,就会有百八十个进程。

每个进程想要执行,就需要消耗一定的系统资源(也就是硬件资源)

每个进程,都是系统资源分配的基本单位~


那么进程在系统中是如何进行管理的呢?

从两个角度来看:

  1. 描述 使用类/结构体,把被管理的一个对象,各个属性都表示出来
  2. 组织 使用数据结构,把这些表示出来的对象串起来~(这是为了后续的增删改查)

而在系统中专门有一个结构体(操作系统内核是使用C/C++)描述进程的属性,而这些结构体统称为 “进程控制块PCB”

使用这些PCB描述进程属性,一个进程就可以使用一个或者多个PCB来表示~

在系统中,就会使用类似于双向链表这样的数据结构来组织多个PCB,其相关操作:

  1. 创建新的进程: 创建 PCB 并且把 PCB 插入到链表中
  2. 销毁进程: 就是将 PCB 从链表上删除并且释放
  3. 展示进程列表: 就相当于遍历链表

要明确认识进程详细的特性的话,可以进一步聊聊 PCB 里面的属性

PCB 是一个非常庞大的结构体,包含很多属性,在Linux中,PCB 被称之为 task_struct

  1. pid :进程的身份标识

    每一个进程都会有一个pid,同一时刻,不同进程之间的pid是不同的

  2. 内存指针

    每一个进程在运行的时候,都会分配一定的内存空间

    也就是说,这个进程的内存空间,具体是在哪里,以及分配的内存空间有哪些部分,每部分的职责都是会有一组指针来进行区分的~


    举一个典型的例子,进程的内存空间,需要有专门的区域存储要执行的指令,以及指令以来的数据~同时还需要存储一些运行时产生的临时数据

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    举个现实生活的例子:老师布置作业,那么同学们在执行任务之前,就需要将作业记录到自己的作业登记本上面~

  3. 文件描述符表

    描述了进程持有的“硬盘资源”是什么样子的~

    类似于顺序表这样的数据结构,有很多元素和文件有关 => 硬盘有关

    一个进程也需要是涉及到硬盘操作,就需要按照文件的方式来操作,当前进程关联了哪些文件,都能操作哪些文件,就是通过文件描述符表来看的


进程持有的cpu资源如何体现?

  1. 分时复用
  2. 并发

早期的操作系统,是一个单任务的操作系统,就是同一时刻只有一个进程能运行,运行下一个进程,就会退出上一个~

一个进程要执行,就是需要cpu来执行上面的指令,早期的电脑,还是单核cpu,一个cpu核心,同一时刻,只能执行一个进程的指令

这里我们可以这样类比

  • cpu核心:舞台
  • 进程:演员
  • 指令:剧本(剧本上有很多幕)

那么我们就可以这样来理解分时复用并发

  1. 分时复用:很多演员,轮流上去演剧,每个演员演完一幕就下来了,腾出地方,给下一个演员去演
  2. 并发:只要演员的轮转速度过快,此时就好像这些演员同时在表演一样

如果两个进程同时在两个cpu核心上,微观上也是"同时执行”,这个情况称为"并行
一个cpu核心上,通过快速轮转调度的方式,执行多个进程,宏观上是"同时执行",微观上有先有后,这个情况称为"并发

咱们作为普通的程序猿平时也不会具体区分并发还是并行.从编程角度来说,底层是并发还是并行,对代码没啥影响…平时也就会统一使用"并发”来代指并行和并发.


PCB属性

PCB中引入了一些属性,用来支持操作系统实现进程调度的效果~~

  1. 进程的状态
  2. 进程的优先级
  3. 进程的上下文
  4. 进程的记账信息

为了容易理解这四个属性,我们给定一个场景:

假设你是一个性感好看有才华的妹妹
有很多男的追求你:
1. 有钱
2. 185黑皮体育生大帅哥
3. 死舔狗
但是没有一个人兼备,但是小孩子才做选择,你全都要,所以你不得不拜罗志祥为师,成为时间管理大师,因此你需要合理规划时间,来确保这三个人不会同时出现(分时复用),同时和三个男的谈恋爱🤣🤣🤣🤣🫣

进程的状态

默认情况下:这三男的,随叫随到,呼之即来,挥之即去

这种情况称之为“就绪状态”,进程时刻准备好,去cpu上执行

而就绪状态,具有两种情况:

  1. 进程在cpu上执行 (你现在在和A约会)
  2. 虽然没执行,但是时刻准备着去cpu上执行 (今天日程安排没有和B约会,但是只要你想要了,call B B立刻出现🥵🥵🥵)

但是如果有一天,在你的安排里面,今天是和C约,但是C去出差了(他也想立刻回来舔你,但是不赚钱怎么舔啊~),此时称这为“阻塞状态

某个进程,某种执行条件不具备,就导致这个进程暂时无法参与cpu的调度执行

进程的优先级

操作系统在调度多个进程的时候并非是一视同仁,有些进程会给更高的优先级,优先调度
我的电脑上同时运行 L O L (优先级更高)和q q(更低)
更好的调配系统资源
把钱花在刀刃上~~
消息晚收一会都不是大问题~~

用上面的场景来看就是:

你在排时间表的时候ABC三个小哥,分配的时间并非是均等的
A我最喜欢分的时间最多,B其次C只会舔,舔腻歪了,分的最少

进程的上下文

引入场景:

有一天,你和A约会,A说,下个月,咱们一起去巴厘岛度假吧~~我说好啊,他说那你准备准备~~
第二天,你和B约会,B说,下个月,他的妈妈过生日,他不知道该买啥礼物,想让我帮忙.你说好啊.他说,那你准备准备
针对A,你做的准备是:准备护照,准备一套性感的泳衣🫣🫣🫣
针对B,你准备一套首饰~~

我又和A约会,A问我,你准备的咋样了?
我又和B约会,B问我,你准备的咋样了?
这时候如果你两个准备颠倒过来了,那就死了,穿帮咯
务必要避免穿帮的情况~~
你需要搞小本本,把每次和这些人约会的状态,具体有哪些事情没搞完,需要下次继续搞,都明确记录好并且在下次约会之前,要温习一下

这也就是上下文~

正经点来看就是:

进程从cpu离开之前,需要保存现场,把当前cpu中各种寄存器的状态,都记录到内存中
等到下次进程回到cpu上执行的时候,此时就可以把保存的这些寄存器的值,恢复回去.进程就会沿着上次执行到的位置,继续往后执行.

也就是:读档、存档

既然讲到这里,有点CPU寄存器那味了~

CPU寄存器

CPU中有些寄存器,属于没有特定含义,就只是用来保存运算的中间结果的,还有些寄存器,是有特定含义,特定作用的.

  1. 保存当前执行到哪个指令(程序计数器)
    是一个2字节/4字节/8字节整数
    这个整数存的是一个内存地址
    内存地址:程序下一条要执行的指令所在的位置:

    exe里面就包含了指令和数据,把exe运行起来,操作系统就会把指令和数据加载到
    内存中.(内存地址)
    CPU就会先从内存中取指令,然后再执行指令.
    初始情况下,程序计数器就指向进程指令的入口(简单粗暴的想象成是main方法)
    每次取完一条指令,程序计数器的值都会自动更新,
    默认情况下,直接指向下一条(顺序执行)
    但是如果遇到跳转类指令jmp,jcmp,call),就会被设置成跳转到的地址…

  2. 维护栈相关的寄存器
    通过这一组(一般是两个)维护当前程序的"调用栈”
    而 栈,也是一块内存,这个内存里就保存当前这个程序方法调用过程中,一系列的关系(也包含局部变量和方法参数…)
    ebp始终指向栈底
    esp始终指向栈顶.修改esp的值就可以实现"入栈”/“出栈”
    (push指令完成上述操作)
    有了这个才知道一个方法执行完毕之后,回到哪里执行.

  3. 其他的通用寄存器了.往往是用来保存计算的中间结果的.

    比如说10+20+30+40假设在算完10+20之后,还没来得及算后面,
    进程调度走了,就需要把保存10+20的寄存器的值给备份到上下文中

实际上一个cpu里面的寄存器也没多少,几十个字节到几百个字节,数据不多,保存好保存,恢复也好恢复 。一般都是直接将这些寄存器都一股脑打包进内存即可,也就是PCB

进程的记账信息

通过优先级机制,对不同的进程分配了不同权重的资源,
有可能会出现极端的情况,所有的资源都给某个进程,其他进程一点都没捞着~~

比如说:一周4天拍给A,2天拍给B,1天放假,C??
如果我一直不搭理C,C对我的热情就会降低可能就会去舔别的女神了.适当的得给他点甜头,得能让它看到点希望~~
我就需要统计一段时间给ABC分配总天数如果发现C特别少,就需要适当的补偿

记账信息,会记录当前进程持有cpu的情况(在cpu执行多久了)就可以作为操作系统调度进程的参考依据~

虚拟地址空间

虚拟地址空间是操作系统中用来给每个进程分配内存的一种技术。它为每个进程提供了一个独立的虚拟内存空间,使得每个进程可以同时存在于主存储器中,而不会相互干扰。

虚拟地址空间的作用主要有以下几个方面:

  1. 内存隔离:每个进程都有自己的虚拟地址空间,使得它们之间的内存彼此独立,相互隔离。这样可以保护进程的私密数据,防止进程之间的相互影响。

  2. 资源管理:虚拟地址空间使操作系统可以更有效地管理内存资源。它允许操作系统灵活地分配和回收内存,并且可以将实际的物理内存分配给不同的进程。

  3. 内存映射:虚拟地址空间允许进程将文件或其他设备映射到其地址空间中。这种机制使得进程可以直接访问这些文件或设备的内容,从而避免了繁琐的文件和设备操作。

  4. 内存保护:虚拟地址空间允许操作系统对进程的内存进行保护。通过设置权限位和访问控制列表,操作系统可以限制进程对内存的访问,防止错误的内存访问或恶意行为。

总而言之,虚拟地址空间提供了一种使得每个进程都拥有自己独立的地址空间的机制,提高了系统的安全性、可靠性和资源管理效率。

不看那么多字,我们用一张图理解:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


至此,通过上述方式,把进程之间给隔离开了.如果某个需求中,确实就需要让多个进程相互配合,此时就不好搞了.
此处就需要引入新的机制,来实现进程之间的通信,
具体的实现方式有很多.都是要借助一个公共空间,完成数据的交互但是每个方式的核心思想都是一样的。

在现今多核CPU的时代,为了实现并发编程,我们需要引入多个进程。

多进程,实现并发编程,效果也是非常理想的。
但是,多进程编程模型,也有明显的缺点,进程太重量,效率不高。

  • - 创建一个进程,消耗时间比较多
    
    - 销毁一个进程,消耗时间也比较多
    
    - 调度一个进程消耗时间也比较多…
    
  • 这些时间都是消耗在申请资源上的,进程是资源分配的基本单位。而分配内存,就是一个大工作~

  • 因为操作系统内部有一定的数据结构,把空闲的内存分块管理好.
    当我们去进行申请内存的时候,系统就会从这样的数据结构中
    如果需要频繁的创建/销毁进程,这个时候年销就不能忽视了
    找到一个大小合适的空闲内存,返回给对应的进程。、

  • 这里虽然通过此处的数据结构,可以一定程度提高效率,整体来说,管理的空间比较多,相比之下还是一个耗时操作~

  • 如果需要频繁的创建/销毁进程,这个时候开销就不能忽视了

    (这一点在早期服务器开发中是非常常见的情况,C++CGI技术,就是这样一种基于多进程的方式实现网站后端)

为了解决这个问题,所以下面我们引出线程这块知识~


线程

线程也叫做“轻量级进程”。

创建、销毁、调度线程都比操作进程更快~

但是线程不能独立存在,而是要依附于进程(也即是说进程包含线程),进程可以包含一个线程,也可以包含多个线程。

一个进程,最开始的时候,至少要有一个线程这个线程负责完成执行代码的工作,也可以根据需要,创建出更多的线程,从而使当前实现"并发编程”的效果~

前面谈到进程调度,前面的讨论都是基于"一个进程里只有一个线程”的情况。
实际上,一个进程中,是可以有多个线程的每个线程,都是可以独立的进行调度的~~
每一个线程,也有状态,优先级,上下文,记账信息…
一个进程,使用PCB表示,一个进程可能使用一个PCB表示,也可能使用多个PCB表示.每个PCB对应到一个线程上,除此之外,前面谈到的pid都是相同的,内存指针,文件描述符表,也是共用一份的~


上述结构,决定了线程的特点:

  1. 每个线程都可以独立的在CPU上面调度执行
  2. 同一个进程的多个线程间,共用同一份内存空间和文件资源

所以说,创建线程的时候,不需要重新申请资源了,直接复用之前已经分配给进程的资源,省去了资源分配的开销,于是创建效率就更高了。

进程中包含线程 => 一个进程由多个 PCB 共同表示 => 每个PCB就用来表示一个线程 => 每个线程都有自己的状态,上下文,优先级,记账信息 =>
每个线程都可以独立的去 CPU 上调度执行 => 这些 PCB 共用了同样的内存指针和文件描述符表 => 创建线程(PCB)不需要重新申请资源 => 创建/销毁效率都更高了

  • 进程是资源分配的基本单位
  • 线程是调度执行的基本单位

一个系统中,可以有很多进程,每个进程,都有自己的资源,一个进程中,可以有很多线程,每个线程都能独立调度,共享内存/硬盘资源


下面我们用图片例子来进一步理解:

设定情景:一张桌子只能坐8个鸡哥,鸡哥们吃坤腿~

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


线程与进程的区别

言归正传,我们来聊聊线程与进程的区别

  1. 进程包含线程,一个进程里面可以有一个线程,也可以有多个线程

  2. 进程和线程,都是用来实现并发编程场景的.但是线程比进程更轻量,更高效

  3. 同一个进程的线程之间,共用同一份的资源(内存+硬盘),这就会省去了申请资源的开销

  4. 进程和进程之间,是具有独立性的,如果一个进程挂了,是不会影响到别人.
    但是线程和线程之间(前提是同一个进程内),是可能会相互影响的.(线程安全问题+线程出现异常)

  5. 进程是资源分配的基本单位,线程是调度执行的基本单位


Java进行多线程编程

线程是操作系统的概念.操作系统提供了一些APl,可以操作线程
Java针对上述系统API进行了封装.(跨平台)

所以我们只需要使用好Thread类,创建Thread对象就可以进一步操作系统内部的线程了。

package Thread;


class MyTread extends Thread{
    @Override
    public void run() {
        //这个方法就是线程的入口方法
        System.out.println("hello world");
    }
}

//演示创建线程
public class Demo1 {
    public static void main(String[] args) {
        Thread t = new MyTread();
        //start 和 run 都是 Thread 的成员
        //run 只是描述了线程的入口(线程主要做什么任务)
        //start 则是真正调用了系统API,在系统中创建线程,让线程再调用run
        t.start();
    }
}

至此多线程(一)前沿知识先到这,接下会持续更新,敬请期待~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1008905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IO day6

1->x.mind 2-> #include <myhead.h> char c; ssize_t res1; //互斥锁 pthread_mutex_t mutexPTHREAD_MUTEX_INITIALIZER; //创建条件变量 pthread_cond_t condPTHREAD_COND_INITIALIZER; int flag0;//0:打印 1&#xff1a;倒置 void* callBack1(void* arg) { …

Python爬虫:获取必应图片的下载链接

文章目录 1. 前言2. 实现思路3. 运行结果 1. 前言 首先&#xff0c;说明一下&#xff0c;本篇博客内容可能涉及到版权问题&#xff0c;为此&#xff0c;小编只说明一下实现思路&#xff0c;至于全部参考代码&#xff0c;小编不粘贴出来。不过&#xff0c;小编会说明详细一些&a…

Mybatis系列之核心分析

文章目录 一、Mybatis的前世1、简述&#xff1a;2、什么是JDBC&#xff1a;3、什么是驱动&#xff1a;4、JDBC的开发步骤&#xff1a;《1》注册和加载数据库驱动《2》获得数据库连接《3》获得语句执行对象&#xff0c;然后执行SQL语句&#xff0c;获取执行结果&#xff0c;最后…

问道管理:机器人产业迎催化 黄金价格或将突破前高

昨日&#xff0c;沪指盘中震动下探&#xff0c;一度跌近1%逼近3100点&#xff0c;尾盘逐步止跌&#xff1b;深成指、创业板指均跌超1%。截至收盘&#xff0c;沪指跌0.45%报3123.07点&#xff0c;深成指跌1.14%报10255.87点&#xff0c;创业板指跌1.14%报2027.73点&#xff0c;科…

SpringBoot2.0(mybatis-plus初始使用)

目录 一&#xff0c;介绍二&#xff0c;SpringBoot2.x整合MybatisPlus Lombok2.1&#xff0c;添加依赖 pom2.2&#xff0c;配置数据库信息 application.properties2.3&#xff0c;工程结构初始化 三&#xff0c;创建接口返回统一对象四&#xff0c;创建bean五&#xff0c;创建…

iTOP-STM32MP157开发板Ubuntu镜像的烧写

由于 Ubuntu 镜像的烧写和之前的 QT 系统存在区别,QT 系统所使用的内核可以不用区分屏幕&#xff0c;而ubuntu 系统不同。所以我们在烧写镜像的时候需要修改对应的内核镜像&#xff0c;我们以烧写 ubuntu18 无桌面版本的镜像为例&#xff0c;镜像存放路径为“iTOP-STM32MP157 开…

Windows10/11强制删除多余的本地连接、以太网

如图进入到网络适配器准备删除多余的网络&#xff0c;发现无法删除&#xff0c;删除按钮是被禁用的。 解决办法 此电脑》右键》管理 找到对应连接下面的名称 设备管理器》网络适配器》Hyper-V Virtual Ethernet Adapter>右键》卸载设备 谨慎操作&#xff0c;卸载错的话…

基于视觉重定位的室内AR导航APP的大创项目思路(2):改进的项目思路——建图和定位分离

文章目录 一、建图二、定位首先是第一种方法&#xff1a;几何方法其次是第二种方法&#xff1a;图像检索方法最后是第三种方法&#xff1a;深度学习方法 前情提要&#xff1a; 是第一次做项目的小白&#xff0c;文章内的资料介绍如有错误&#xff0c;请多包含&#xff01; 一、…

递归学习——记忆化搜索

目录 ​编辑 一&#xff0c;概念和效果 二&#xff0c;题目 1.斐波那契数 1.题目 2.题目接口 3.解题思路 2.不同的路径 1.题目 2.题目接口 3.解题思路 3.最长增长子序列 1.题目 2.题目接口 3.解题思路 4.猜数字游戏II 1.题目 2.题目接口 3.解题思路 总结&a…

2.docker基础使用命令

请点击滑动滚轮&#xff1a;放大查看 PS:发现滚轮不能放大了&#xff0c;这傻B的csdn&#xff0c;越做越垃圾了。。。 来这个地址看吧&#xff1a;https://img-blog.csdnimg.cn/7a5eb5a1eca4484fa0faa73b398257bd.png&#xff0c;滑动滚轮放大 ​ 源文件&#xff1a; 点击下载…

快速幂 c++

一般大家写都是 int ans 1; for (int i 1; i < a; i )ans * x;时间复杂度 但是这对于我们还不够&#xff0c;我们要 首先我们得知道一个数学知识 那么求 就有以下递归式 a 能被2整除 a 不能被2整除 (这里a/2是整除) 所以每次都调用 不就是么 最后补充一个东西…

【Java从入门到精通】这也许就是Java火热的原因吧!

前言&#xff1a;Java是一种高级的、面向对象的、可跨平台的程序设计语言。Java根据技术类别可划分为以下几类&#xff1a;JavaSE&#xff08;Standard Edition&#xff0c;标准版&#xff09;&#xff1a;支持面向桌面、嵌入式和移动设备的应用程序开发&#xff1b;JavaEE&…

Apollo介绍和入门

文章目录 Apollo介绍配置中心介绍apollo介绍主流配置中心功能特性对比 Apollo简介 入门简单的执行流程Apollo具体的执行流程Apollo对象执行流程分步执行流程 核心概念应用&#xff0c;环境&#xff0c;集群&#xff0c;命名空间企业部署方案灰度发布全量发布 配置发布的原理发送…

MyBatis基础之概念简介

文章目录 基本概念1. 关于 MyBatis2. MyBatis 的体系结构3. 使用 XML 构建 SqlSessionFactory4. SqlSession5. 默认的别名6. 补充 [注意] 放前面前 很多人可能在使用 MyBatis-plus 进行代码开发&#xff0c;MyBatis的这部分内容是用来更好的讲述之后的内容。 基本概念 1. 关于…

无涯教程-JavaScript - ISODD函数

描述 如果数字为奇数,则ISODD函数返回TRUE,如果数字为偶数,则返回FALSE。 语法 ISODD (number) 争论 Argument描述Required/OptionalNumber 要测试的值或表达式。 如果number不是整数,则将其截断。 Required Notes 您可以在执行计算之前使用此功能测试单元格的内容。 如果…

微服务 第一章 Java线程池技术应用

系列文章目录 第一章 Java线程池技术应用 文章目录 系列文章目录[TOC](文章目录) 前言1、Java创建线程方式回顾1.1、继承Thread类(只运行一次)1.1.1、改造成主线程常驻&#xff0c;每秒开启新线程运行1.1.2、匿名内部类1.1.3、缺点1.1.4、扩展知识&#xff1a;Java内部类1.1.4…

Python 内置函数详解 (2) 逻辑运算

近期在外旅游,本篇是出发编辑的,准备定时发布用,不完整,旅游回来后再补充。 Python 内置函数 Python3.11共有75个内置函数,其来历和分类请参考:Python 新版本有75个内置函数,你不会不知道吧_Hann Yang的博客-CSDN博客https://blog.csdn.net/boysoft2002/article/detai…

SOLIDWORKS PDM—数据库的备份计划

SOLIDWORKS产品数据管理 (PDM) 解决方案可帮助您控制设计数据&#xff0c;并且从本质上改进您的团队就产品开发进行管理和协作的方式。使用 SOLIDWORKS PDM Professional&#xff0c;您的团队能够&#xff1a;1. 安全地存储和索引设计数据以实现快速检索&#xff1b;2. 打消关于…

Linux——进程间通信(管道及共享内存)

目录 0. 前言 1. 进程通信的目的 2. 进程通信发展及分类 3. 进程通信匿名管道 3.1 什么是管道&#xff1f; 3.2 匿名管道系统调用 3.3 fork后子进程继承&#xff08;基于内存级&#xff09; 3.4 站在文件描述符角度-深度理解管道 3.5 站在内核角度-管道本质 3.6 父子…

电动车彻底取代燃油车?瑞士限制,中国每天烧7辆,现实不乐观

随着新能源汽车在国内汽车市场的销量占比突破三成&#xff0c;业界对于电动汽车取代燃油车相当乐观&#xff0c;然而电动汽车存在的不少问题却不容忽视&#xff0c;这正逐渐成为电动汽车普及的巨大障碍。 电动汽车如今面临的问题不少&#xff0c;最让消费者吐槽的是充电问题&am…