HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
随笔
关于作者
  • 技术

    • 4G的机器上申请8G的内存
    • CPU和内存交互
    • GraalVM看法
    • Gradle项目导入
    • Linux-awk使用案例
    • Linuxmint安装
    • Ubuntu安装
    • VMware安装虚拟机
    • springboot异步编程
    • win+Ubuntu双系统安装
    • 企业级监控prometheus
    • 单核CPU是否支持多线程
    • 虚拟机软件
  • 所思所悟

  • 其他

  • 读书笔记

  • ChatGpt
  • openClaw使用
  • 随笔
  • 技术
#CPU #单核
码农阿雨
2022-11-13
目录

单核CPU是否支持多线程

先说结论:支持。

单核 CPU 是支持 Java 多线程的。操作系统通过时间片轮转的方式,将 CPU 的执行时间分配给不同的线程。尽管单核 CPU 在任意时刻只能执行一个任务,但通过快速在线程之间切换,可以营造出多个任务“同时”运行的假象。


此前多线程专栏聊过,多线程执行会比单线程执行更快,能提高并发速度。

但是并不是启动越多的线程,并发就越高,线程的创建、销毁、上下文切换频繁,也会有很大的开销,反而你的程序的处理能力就会下降。

在单核 CPU 上,同一时刻只能有一个线程在运行,如果任务是 CPU 密集型的,那么开很多线程会影响效率;如果任务是 IO 密集型的,那么开很多线程会提高效率。

# 超线程

现代CPU除了处理器核心之外还包括寄存器、L1L2缓存这些存储设备、浮点运算单元、整数运算单元等一些辅助运算设备以及内部总线等。一个多核的CPU也就是一个CPU上有多个处理器核心,就意味着程序的不同线程需要经常在CPU之间的外部总线上通信,同时还要处理不同CPU之间不同缓存导致数据不一致的问题。

超线程这个概念是Intel提出的,简单来说是在一个CPU上真正的并发两个线程,由于CPU都是分时的(如果两个线程A和B,A正在使用处理器核心,B正在使用缓存或者其他设备,那AB两个线程就可以并发执行,但是如果AB都在访问同一个设备,那就只能等前一个线程执行完后一个线程才能执行)。实现这种并发的原理是 在CPU里加了一个协调辅助核心,根据Intel提供的数据,这样一个设备会使得设备面积增大5%,但是性能提高15%~30%。

# 时间片

CPU 的核心在同一个时间内,只会执行单一线程。

但是现在的系统都是多任务系统,需要同时执行多道作业,作业数往往大于机器的CPU数。

例如 windows ,你同时着 微信、文件夹、输入法、浏览器、typora、git,来回切换,看着就像是同时进行的,但是CPU的核心却只有几个。

这就是 时间轮询片,简称 时间片 的原理。

时间片 是CPU分配 给线程执行的时间。

# 上下文切换

切换分多种情况:

  • 线程切换,同一进程中的两个线程之间的切换
  • 进程切换,两个进程之间的切换
  • 模式切换,在给定线程中,用户模式和内核模式的切换
  • 地址空间切换,将虚拟内存切换到物理内存

假如是单核CPU的情况下,当微信拿到了时间片,便执行;如果浏览器此时需要执行,CPU就会调度,进行 线程切换。

CPU切换前把当前任务的状态保存下来,以便下次切换回这个任务时可以再次加载这个任务的状态,然后加载下一任务的状态并执行。任务的状态保存及再加载,这段过程就叫做上下文切换。

那是如何保存当前状态的呢?

每个线程都有一个程序计数器(记录要执行的下一条指令),一组寄存器(保存当前线程的工作变量),堆栈(记录执行历史,其中每一帧保存了一个已经调用但未返回的过程)。

  • 寄存器 是 CPU 内部的数量较少但是速度很快的内存(与之对应的是 CPU 外部相对较慢的 RAM 主内存)。寄存器通过对常用值(通常是运算的中间值)的快速访问来提高计算机程序运行的速度。

我们常说的 内存、主存,都是只 RAM内存条的内存

  • 程序计数器是一个专用的寄存器,用于表明指令序列中 CPU 正在执行的位置,存的值为正在执行的指令的位置或者下一个将要被执行的指令的位置。
    • 挂起当前任务(线程/进程),将这个任务在 CPU 中的状态(上下文)存储于内存中的某处
    • 恢复一个任务(线程/进程),在内存中检索下一个任务的上下文并将其在 CPU 的寄存器中恢复
    • 跳转到程序计数器所指向的位置(即跳转到任务被中断时的代码行),以恢复该进程在程序中]

线程上下文切换会有什么问题呢?

上下文切换会导致额外的开销,常常表现为高并发执行时速度会慢串行,因此减少上下文切换次数便可以提高多线程程序的运行效率。

  • 直接消耗:指的是CPU寄存器需要保存和加载, 系统调度器的代码需要执行, TLB实例需要重新加载, CPU 的pipeline需要刷掉
  • 间接消耗:指的是多核的cache之间得共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小

# 引起线程上下文切换的因素

  • 当前执行任务(线程)的时间片用完之后,系统CPU正常调度下一个任务

  • 中断处理,在中断处理中,其他程序”打断”了当前正在运行的程序。当CPU接收到中断请求时,会在正在运行的程序和发起中断请求的程序之间进行一次上下文切换。中断分为硬件中断和软件中断,软件中断包括因为IO阻塞、未抢到资源或者用户代码等原因,线程被挂起。

  • 用户态切换,对于一些操作系统,当进行用户态切换时也会进行一次上下文切换,虽然这不是必须的。

  • 多个任务抢占锁资源,在多任务处理中,CPU会在不同程序之间来回切换,每个程序都有相应的处理时间片,CPU在两个时间片的间隔中进行上下文切换

因此优化手段有:

  • 无锁并发编程,多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash取模分段,不同的线程处理不同段的数据
  • CAS算法,Java的Atomic包使用CAS算法来更新数据,而不需要加锁
  • 使用最少线程
  • 协程,单线程里实现多任务的调度,并在单线程里维持多个任务间的切换

合理设置线程数目既可以最大化利用CPU,又可以减少线程切换的开销。

  • 高并发,低耗时的情况,建议少线程。
  • 低并发,高耗时的情况:建议多线程。
  • 高并发高耗时,要分析任务类型、增加排队、加大线程数

# 线程调度

操作系统的线程调度主要分为两种方式:

  • 抢占式调度(Preemptive Scheduling):线程的执行权由操作系统控制。系统会根据线程优先级和分配给每个线程的时间片,强制暂停(抢占)当前正在运行的线程,并切换到下一个线程。这种切换通常由系统时钟中断触发,也可能因高优先级事件(如 I/O 操作完成)而进行。抢占式调度的优点是公平性较好,能够避免单个线程长期占用 CPU 导致其他线程阻塞;缺点是需要频繁进行上下文切换,会带来一定的性能开销。
  • 协同式调度(Cooperative Scheduling):线程的执行权由线程自身控制。线程在执行完任务后,必须主动通知系统切换到另一个线程。如果某个线程在执行耗时操作或进入无限循环而不主动释放 CPU,整个系统就可能被阻塞。协同式调度的优点是上下文切换开销较小,实现相对简单;缺点是公平性差,可靠性低,容易导致系统响应不及时。

Java 采用的线程调度方式是抢占式的。严格来说,Java 语言规范并没有强制规定必须使用抢占式调度,但在主流的操作系统(如 Windows、Linux、macOS)上,Java 线程映射到操作系统内核线程,其调度行为完全由操作系统的线程调度器负责。JVM 本身不直接参与线程调度决策,而是将线程的管理委托给底层的操作系统。操作系统通常会基于线程优先级和时间片来调度线程的执行,优先级较高的线程获得 CPU 时间片的机会相对更多。

阅读全文
×

(为防止恶意爬虫)
扫码或搜索:HelloCoder
发送:290992
即可永久解锁本站全部文章

解锁
#CPU#单核
上次更新: 2026-03-20 10:18:13
最近更新
01
《LeetCode 101》
03-20
02
《LeetCode CookBook》
03-20
03
IDEA、Golang、Pycharm破解安装
03-20
更多文章>
Theme by Vdoing | Copyright © 2020-2026 码农阿雨
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式