深蓝黑背景封面。左侧是作业排队示意:上方四条横向条形代表四个同时到达的作业,长度与执行时间成正比,依次标注「J1 · 1 小时」「J2 · 3 小时」「J3 · 5 小时」「J4 · 7 小时」,条形左侧竖排小字「同时到达」;中间一条向下箭头标注「SJF:短作业优先,按 1 → 3 → 5 → 7 执行」;下方一条迷你甘特条按 1、3、5、7 的比例分段,段间刻度写完成时刻 1、4、9、16;最底一行青色结论「平均周转 = (1+4+9+16) ÷ 4 = 7.5 小时」。右侧大标题「四个作业排队执行,平均周转时间怎么算」,副标题「短作业优先(SJF)调度下的逐步推演」,小注「周转时间 = 完成时刻 − 到达时刻,同时到达即等于完成时刻」。左上标注「面试经典 · 操作系统」,右上圆角胶囊标注「Day 05 · 12-02」。 图 1:本文封面,SJF 调度下的执行顺序与平均周转时间结论。

先说明属性:这是一篇笔试真题复盘,题目出自本地历史笔试资料整理,与任何公司的当前招聘无关。原题以选择题形式出现:现有 4 个同时到达的作业 J1、J2、J3、J4,执行时间分别是 1、3、5、7 小时,系统按单道方式运行并采用短作业优先(SJF)算法,问平均周转时间是多少。选项里最迷惑人的是 8 和 6 这类整数——它们分别对应「顺序执行直接平均」和「算错口径」的典型错法。这类题真正考的不是算术,是周转时间的定义和 SJF 的执行顺序推演。

先立定义:周转时间 = 完成时刻 − 到达时刻。

这是整道题的钥匙。四个作业同时到达,到达时刻都是 0,所以每个作业的周转时间就等于它的完成时刻。很多错选都错在把周转时间当成执行时间,或者当成等待时间——执行时间 1、3、5、7 的平均是 4,等待时间又是另一笔账,口径一混全盘皆错。单道方式运行意味着同一时刻只有一个作业在执行,没有并发,执行顺序就是一条排队的直线。

第一步:确定 SJF 的执行顺序。

短作业优先,顾名思义是谁的执行时间短谁先跑。四个作业同时到达、全部在队列里,没有新到达插队的问题,所以执行顺序就是执行时间的升序:J1(1 小时)→ J2(3 小时)→ J3(5 小时)→ J4(7 小时)。

第二步:逐个算完成时刻。

  • J1 从 0 时刻开始,执行 1 小时,完成于时刻 1,周转时间 1;
  • J2 从时刻 1 开始,执行 3 小时,完成于时刻 4,周转时间 4;
  • J3 从时刻 4 开始,执行 5 小时,完成于时刻 9,周转时间 9;
  • J4 从时刻 9 开始,执行 7 小时,完成于时刻 16,周转时间 16。

第三步:取平均。(1 + 4 + 9 + 16) ÷ 4 = 30 ÷ 4 = 7.5 小时。

写一段程序把这个推演跑一遍,顺便验证一个更强的结论(Node.js 22.15.0,node sjf.js):

SJF(按执行时间升序):
J1(耗时1h,完成于1h) → J2(耗时3h,完成于4h) → J3(耗时5h,完成于9h) → J4(耗时7h,完成于16h)
  平均周转时间 = (1+4+9+16)/4 = 7.5 小时
FCFS(假设按 J4 J3 J2 J1 顺序到达):
J4(耗时7h,完成于7h) → J3(耗时5h,完成于12h) → J2(耗时3h,完成于15h) → J1(耗时1h,完成于16h)
  平均周转时间 = (7+12+15+16)/4 = 12.5 小时
枚举全部 24 种顺序:最优平均周转 7.5 小时,最差 12.5 小时

注意对照组:如果改用先来先服务(FCFS),且长作业恰好排在前面,平均周转时间直接涨到 12.5 小时,比 SJF 多出整整 5 小时。更关键的是最后一行——把四个作业的全部 24 种执行顺序枚举一遍,7.5 小时就是全局最小值。

为什么 SJF 一定最优:相邻交换论证。

这不是枚举出来的巧合,有可证明的道理。假设某个排列里一个长作业 L 排在一个短作业 S 前面,交换它们俩的位置:排在它们前面的作业不受影响,排在它们后面的作业整体提前了(L − S)的时间;而 L 和 S 自己的完成时刻之和也变小了——交换前两者完成的时刻之和比交换后多出 L − S。总周转时间严格变小。也就是说,任何「长在前、短在后」的相邻逆序都可以通过交换改进,所以最优排列里不可能存在逆序,唯一没有逆序的排列就是执行时间升序。这个论证模板可以套用到一大批「排队顺序优化」类题目上。

深蓝黑背景概念图。顶部小字「概念图 · 短作业优先调度」,主标题「先排短的,总等待就被压到最小」。上半部分是 SJF 甘特图:横轴 0 到 16 小时,四个色段依次是 J1(0~1)、J2(1~4)、J3(4~9)、J4(9~16),每段右端下方标注完成时刻 1、4、9、16;右侧公式卡写「平均周转 = (1+4+9+16) ÷ 4 = 7.5 小时」,并注明四个作业同时到达,周转时间等于完成时刻。中部是 FCFS 对照甘特图:假设长作业先排队,按 J4、J3、J2、J1 执行,色段为 0~7、7~12、12~15、15~16,完成时刻 7、12、15、16,平均周转 = (7+12+15+16) ÷ 4 = 12.5 小时,比 SJF 多出 5 小时。底部左侧是「为什么 SJF 最短」的交换论证:若一个长作业排在相邻短作业前面,交换两者后两者的完成时刻之和必然变小,所以最优排列一定是执行时间升序;底部右侧小字提醒 SJF 的两个现实代价:需要预知执行时间、长作业可能长期饥饿。 图 2:SJF 与 FCFS 甘特图对照、最优性的交换论证与现实代价。

答题之外的追问:SJF 的现实代价。

笔试题到这里就结束了,但面试官往往会追问一句「既然 SJF 最优,操作系统为什么不全用它」。两个硬伤:一是它需要预知作业的执行时间,现实里调度器拿不到这个信息,只能用历史行为估算;二是长作业可能长期饥饿——只要短作业源源不断到达,长作业就永远轮不上,需要老化(aging)之类的机制补偿。下次遇到「周转时间」三个字,先默念一遍公式再动笔:完成时刻减到达时刻。你在笔试里被这类调度题坑过吗?错在了定义还是顺序?欢迎聊聊。