[TOC]
巨大的建筑,总是由一木一石叠起来的,我们何妨做做这一木一石呢?我时常做些零碎事,就是为此。单是说不行,要紧的是做
鲁迅.png
目录索引
- 为什么随机IO比顺序IO要慢很多?
- 我们经常说“上下文切换”导致系统开销大,什么是上下文切换 ?
1. 为什么随机IO比顺序IO要慢很多?
涉及到磁盘IO操作的时候, 有一点不得不考虑的就是IO的性能问题会不会成为整个系统的瓶颈,所以大多数情况下都期望的是IO操作是顺序IO而不是随机IO,比如Mysql范围查找使用索引,Kafka存储消息追加日志等等。那么为什么都期望顺序IO来进行读写呢?
- 假设我们需要找到一些数据,这些数据可能分散在磁盘的不同页的不同扇区中,那么我们就需要一个个的寻址,找到对应的页对应的扇区才能找到一块数据,这是找到一块数据的过程,这个过程一直重复直到找到我们需要的所有数据。这种情况就是随机IO,这个寻址的过程会占用太多的时间。
- 假设我们要找到一些数据,这些数据是挨着的,我们只需要寻址到第一块数据,就能一次性的把我们需要的数据都找到,这个就是顺序IO,这个过程只需要一次寻址就行,会大大节省时间。
例子:小明同学身兼多个课代表职务,物理、化学、生物、数学等等。期末考试的试卷要发下来了,小明到老师的办公室去领试卷。随机IO就是不同老师的办公室在不同楼的不同楼层,小明需要一个楼一个楼的去爬然后才能带回来试卷。顺序IO就是这些老师都在同一个办公室里或者办公室是挨着的,小明只需要爬一次楼就可以取回全部的试卷。
2. 我们经常说“上下文切换”导致系统开销大,什么是上下文切换 ?
啥是上下文以及上下文切换
在单处理器时代的时候,操作系统就能处理多线程并发任务。处理器给每个线程分配 CPU 时间片(Time Slice),线程在分配获得的时间片内执行任务。CPU 时间片是 CPU 分配给每个线程执行的时间段,一般为几十毫秒。在这么短的时间内线程互相切换,我们根本感觉不到,所以看上去就好像是同时进行的一样。也就是说,时间片决定了一个线程可以连续占用处理器运行的时长。当一个线程的时间片用完了,或者因自身原因被迫暂停运行了,这个时候,另外一个线程(可以是同一个线程或者其它进程的线程)就会被操作系统选中,来占用处理器。这种一个线程被暂停剥夺使用权,另外一个线程被选中开始或者继续运行的过程就叫做上下文切换(Context Switch)。而暂停和调度的时候,需要保存和恢复的进度信息就是上下文,包括了 寄存器存储内容、程序计数器存储内容等等。
从线程的生命周期开始说起:

线程主要有“新建”(NEW)、“就绪”(RUNNABLE)、“运行”(RUNNING)、“阻塞”(BLOCKED)、“死亡”(DEAD)五种状态。到了 Java 层面它们都被映射为了 NEW、RUNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINADTED 等 6 种状态。在这个运行过程中,线程由 RUNNABLE 转为非 RUNNABLE 的过程就是线程上下文切换。
一个线程的状态由 RUNNING 转为 BLOCKED ,再由 BLOCKED 转为 RUNNABLE ,然后再被调度器选中执行,这就是一个上下文切换的过程。当一个线程从 RUNNING 状态转为 BLOCKED 状态时,我们称为一个线程的暂停,线程暂停被切出之后,操作系统会保存相应的上下文,以便这个线程稍后再次进入 RUNNABLE 状态时能够在之前执行进度的基础上继续执行。当一个线程从 BLOCKED 状态进入到 RUNNABLE 状态时,我们称为一个线程的唤醒,此时线程将获取上次保存的上下文继续完成执行。
比如如下操作都会触发线程上下文的切换
sleep()
wait()
yield()
join()
park()
synchronized
lock
JVM GC - STW
系统开销具体发生在切换过程中的哪些具体环节,总结如下:
- 操作系统保存和恢复上下文
- 调度器进行线程调度
- 处理器高速缓存重新加载
- 上下文切换也可能导致整个高速缓存区被冲刷,从而带来时间开销。
例子(单核CPU版):
- 9:00-10:00 团队A在会议室开会,会议室墙壁上留下了团队A的一些记录,到了10点了,A知道自己预定的时间用完了,于是切出离开,但是这些记录都是很重要的讨论结果,于是员工先拍照暂时记下来
- 10:00-11:00 团队B切入来到了会议室开会,此时墙壁上留下来的只有团队B的沟通结论,到了11:00 团队B知道自己时间到了,于是另一个员工拍照记下来团队B的沟通结论。
- 11:00-12:00 团队A又来了会议室,此时员工A赶紧将拍照记下来的,再次恢复写到墙壁上。
分析:团队A和团队B就是2个不同的线程,会议室就是CPU,而他们预约的时间就是CPU时间片,墙壁上的记录就是当发生切换时要保存/恢复的上下文信息。
当11:00-12:00 团队A回到会议室,此时就是被再次调度了,这个时候为了保证会议室的连贯性(没必要从头再讨论一遍),需要将之前团队A的记录恢复到墙壁上。这整个过程,就是发生了上下文的切换。
最后,针对频繁的上下文切换,有哪些优化的方向:
- 减少锁持有时间,比如无关代码不要放在锁的范围内
- 优化JVM的分配情况,减少 GC 次数,GC会引起上下文切换
- 合理设置线程池大小,避免创建过多的线程,因为一旦线程池的工作线程总数超过系统所拥有的处理器数量,就会导致过多的上下文切换
- 尽量将锁粒度拆分得更小一些,以此避免所有线程对一个锁资源的竞争过于激烈
网友评论