Homeβ€Ί DSAβ€Ί SJF Scheduling β€” Shortest Job First (Preemptive and Non-Preemptive)

SJF Scheduling β€” Shortest Job First (Preemptive and Non-Preemptive)

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: Scheduling β†’ Topic 2 of 4
Learn SJF (Shortest Job First) and SRTF scheduling algorithms.
πŸ§‘β€πŸ’» Beginner-friendly β€” no prior DSA experience needed
In this tutorial, you'll learn:
  • SJF non-preemptive: when CPU free, pick shortest burst among arrived processes.
  • SRTF (preemptive): preempt when new process has shorter remaining time than current.
  • SJF/SRTF minimise average waiting time β€” provably optimal.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑ Quick Answer
SJF is like a smart cashier who, when finishing a customer, always picks the next customer with the fewest items. This minimises the total time everyone spends waiting. Preemptive SJF (SRTF) is even smarter β€” if a new short customer arrives while serving a longer one, the cashier switches immediately.

SJF is provably optimal for average waiting time among all non-preemptive scheduling algorithms β€” and SRTF is optimal among all algorithms including preemptive ones. This optimality is not a heuristic or approximation; it follows directly from an exchange argument. If any solution does not follow SJF order, you can swap adjacent jobs to improve average waiting time.

The practical problem: burst times are not known in advance. Linux's CFS (Completely Fair Scheduler) approximates SJF by tracking vruntime β€” the virtual runtime each process has accumulated β€” and always scheduling the process with the smallest vruntime. It is SJF-like without requiring burst time prediction, at the cost of true optimality.

Non-Preemptive SJF

When the CPU becomes free, pick the arrived process with the shortest burst time.

sjf.py Β· PYTHON
1234567891011121314151617181920212223242526272829303132333435
import heapq

def sjf_non_preemptive(processes: list[dict]) -> list[dict]:
    procs = sorted(processes, key=lambda p: p['arrival'])
    results = []
    current_time = 0
    heap = []  # (burst, arrival, process)
    i = 0
    while i < len(procs) or heap:
        # Add all processes that have arrived
        while i < len(procs) and procs[i]['arrival'] <= current_time:
            heapq.heappush(heap, (procs[i]['burst'], procs[i]['arrival'], procs[i]))
            i += 1
        if not heap:
            current_time = procs[i]['arrival']
            continue
        burst, arrival, p = heapq.heappop(heap)
        start = current_time
        ct  = start + p['burst']
        tat = ct - p['arrival']
        wt  = tat - p['burst']
        current_time = ct
        results.append({**p, 'start': start, 'ct': ct, 'tat': tat, 'wt': wt})
    return results

processes = [
    {'pid':'P1','arrival':0,'burst':8},
    {'pid':'P2','arrival':1,'burst':4},
    {'pid':'P3','arrival':2,'burst':2},
    {'pid':'P4','arrival':3,'burst':1},
]
results = sjf_non_preemptive(processes)
for r in results:
    print(f"{r['pid']}: WT={r['wt']}, TAT={r['tat']}")
print(f"Avg WT: {sum(r['wt'] for r in results)/len(results):.2f}")
β–Ά Output
P1: WT=0, TAT=8
P4: WT=7, TAT=8
P3: WT=8, TAT=10
P2: WT=11, TAT=15
Avg WT: 6.50

Preemptive SJF β€” SRTF

Shortest Remaining Time First preempts the running process when a new process arrives with shorter remaining time.

srtf.py Β· PYTHON
1234567891011121314151617181920212223242526272829303132
def srtf(processes: list[dict]) -> list[dict]:
    """Shortest Remaining Time First β€” preemptive SJF."""
    procs = [dict(p, remaining=p['burst'], start=-1) for p in processes]
    procs.sort(key=lambda p: p['arrival'])
    n = len(procs)
    time = 0
    completed = 0
    current = None
    results = {}
    while completed < n:
        # Find process with shortest remaining time among arrived
        arrived = [p for p in procs if p['arrival'] <= time and p['remaining'] > 0]
        if not arrived:
            time += 1
            continue
        current = min(arrived, key=lambda p: p['remaining'])
        if current['start'] == -1:
            current['start'] = time
        current['remaining'] -= 1
        time += 1
        if current['remaining'] == 0:
            ct  = time
            tat = ct - current['arrival']
            wt  = tat - current['burst']
            results[current['pid']] = {**current,'ct':ct,'tat':tat,'wt':wt}
            completed += 1
    return list(results.values())

results = srtf(processes)
for r in results:
    print(f"{r['pid']}: WT={r['wt']}, TAT={r['tat']}")
print(f"Avg WT: {sum(r['wt'] for r in results)/len(results):.2f}")
β–Ά Output
P1: WT=9, TAT=17
P2: WT=1, TAT=5
P3: WT=0, TAT=2
P4: WT=0, TAT=1
Avg WT: 2.50

Why SJF is Optimal

SJF minimises average waiting time among all non-preemptive algorithms. Proof by exchange argument: if a longer job runs before a shorter one, swapping them reduces total waiting time. SRTF minimises average waiting time among ALL scheduling algorithms (preemptive and non-preemptive).

⚠️
Starvation ProblemSJF can starve long processes β€” if short processes keep arriving, long ones never run. Solution: aging β€” gradually increase priority of waiting processes over time.

Burst Time Prediction

Real schedulers don't know burst times in advance. Prediction via exponential averaging: Ο„_{n+1} = Ξ± Γ— t_n + (1-Ξ±) Γ— Ο„_n where t_n is actual burst time, Ο„_n is predicted, Ξ± controls responsiveness (typically 0.5).

🎯 Key Takeaways

  • SJF non-preemptive: when CPU free, pick shortest burst among arrived processes.
  • SRTF (preemptive): preempt when new process has shorter remaining time than current.
  • SJF/SRTF minimise average waiting time β€” provably optimal.
  • Starvation: long processes may never run if short ones keep arriving β€” fix with aging.
  • Burst time is unknown in practice β€” predicted via exponential averaging of past behaviour.

Interview Questions on This Topic

  • QProve that SJF minimises average waiting time.
  • QWhat is the difference between SJF and SRTF?
  • QHow does aging solve the starvation problem in SJF?
  • QCalculate average waiting time for SRTF: P1(AT=0,BT=8), P2(AT=1,BT=4), P3(AT=2,BT=2).

Frequently Asked Questions

Is SJF or SRTF used in real operating systems?

Not directly β€” real OSes don't know burst times. However, Linux's Completely Fair Scheduler (CFS) approximates SJF behaviour by tracking process runtime and favouring processes that have used less CPU time recently.

πŸ”₯
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousFCFS Scheduling β€” First Come First ServedNext β†’Round Robin Scheduling Algorithm
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged