Java高频面试之并发编程-06
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶
面试官:线程上下文切换是什么?
线程上下文切换(Thread Context Switching)是操作系统中 CPU 从一个线程切换到另一个线程执行的过程。它本质上是保存当前线程的执行状态(上下文),并加载下一个线程的状态,使得 CPU 可以继续执行新线程的任务。
为什么需要上下文切换?
- 多线程并发需求:CPU 核心数有限(如 4 核),但操作系统需要同时运行成百上千的线程,必须通过快速切换线程来模拟“并行”。
- 线程状态变化:线程可能主动让出 CPU(如等待 I/O、锁、睡眠)或被系统强制切换(如时间片用完)。
上下文切换的步骤
- 保存当前线程的上下文
将当前线程的寄存器(如程序计数器 PC、栈指针 SP)、状态信息等保存到内存(通常是线程控制块 TCB)。 - 选择下一个要运行的线程
由调度器(Scheduler)根据算法(如时间片轮转、优先级)选择就绪队列中的线程。 - 加载新线程的上下文
从新线程的 TCB 中恢复寄存器、程序计数器、栈指针等,开始执行新线程。
触发上下文切换的常见场景
- 时间片耗尽:CPU 分配给线程的时间片用完,强制切换。
- 线程主动让出 CPU:如调用
sleep()
,wait()
,yield()
,或等待 I/O 完成。 - 高优先级线程抢占:高优先级线程就绪时,可能抢占当前线程。
- 中断处理:硬件中断(如磁盘 I/O 完成)触发中断服务程序执行。
上下文切换的性能代价
- 直接开销:保存/恢复上下文需要 CPU 时间(通常数百到数千时钟周期)。
- 间接开销:切换后 CPU 缓存(Cache)可能失效,导致新线程运行时需要重新加载数据到缓存。
- 高并发时问题放大:频繁切换会导致 CPU 时间浪费在管理线程而非执行任务,严重时引发性能下降(如 C10k 问题)。
如何减少上下文切换?
- 减少线程数:
使用线程池(如ThreadPoolExecutor
)复用线程,避免频繁创建/销毁线程。 - 非阻塞 I/O:
使用异步 I/O(如 NIO)代替同步阻塞 I/O,减少线程等待。 - 无锁编程:
使用无锁数据结构(如ConcurrentLinkedQueue
)或 CAS 操作,减少线程竞争。 - 协程(用户态线程):
协程的切换在用户态完成(无需内核介入),开销远低于线程切换(如 Go 的 Goroutine)。
示例:上下文切换的影响
假设一个 4 核 CPU 运行 100 个活跃线程:
- 每个时间片假设为 10ms,每 10ms 可能触发一次切换。
- 频繁切换会导致 CPU 忙于切换而非执行有效任务,实际吞吐量下降。
总结
核心点 | 说明 |
---|---|
本质 | CPU 从一个线程切换到另一个线程时保存和恢复状态的过程。 |
性能关键 | 频繁切换会显著降低系统吞吐量,尤其在多线程高并发场景。 |
优化方向 | 减少线程数、异步 I/O、无锁设计、协程等。 |
与进程切换区别 | 进程切换涉及虚拟内存等资源切换,开销更大;线程切换仅切换线程私有资源。 |