Skip to lab content

WEEK 14 · OBJECT ORIENTED PROGRAMMING LAB

Priority-based job scheduling

EXPERIMENT 27Java 11+Console program

THE SYLLABUS TASK

What you need to build

Design a job scheduling system.
Full syllabus requirements: classes, fields and methods
Design a job scheduling system. Class: Task with attributes as taskId, runTime, priority Requirements: • Higher priority jobs execute first • Use Comparator to rank tasks • Print execution order with runTime
Input format & conventions

n (0–50), then task ID, positive runTime and priority (0–100) for every task, one value per line. IDs unique. Larger priority executes first; ties use smaller task ID. All tasks are available initially.

UNDERSTAND THE IDEA

Explanation

PriorityQueue uses a Comparator to choose the highest-priority task at each removal. The program repeatedly polls/removes rather than iterating the queue, because queue iteration is not guaranteed to be priority order.

The scheduler is a nonpreemptive simulation. runTime is printed and added to cumulative completion time; the program does not actually sleep for that duration or model arrivals during execution.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Read validated tasks and reject duplicate IDs.
  2. Insert them into a comparator-driven PriorityQueue.
  3. Remove the highest-priority remaining task.
  4. Add its runTime to a long cumulative clock.
  5. Print execution and completion order until the queue is empty.

SEE THE CORE OPERATION

Flowchart

This chart focuses on the named operation or core OOP decision. Diamonds show conditions; labeled branches show outcomes. Read it with the complete algorithm and source for input handling and other operations. Scroll within the diagram on a phone.

Priority-queue scheduling

Priority-queue scheduling

Open full-size flowchart ↗

Java 11+

Complete Java source

Main.java

Download .java
import java.util.*;

public class Main {
    private static final Scanner IN = new Scanner(System.in).useLocale(Locale.ROOT);
    static String text() {
        if (!IN.hasNextLine()) throw new IllegalArgumentException("Incomplete input.");
        String value = IN.nextLine().trim();
        if (value.isEmpty() || value.length() > 200) throw new IllegalArgumentException("Text must have 1–200 characters.");
        return value;
    }
    static int integer(int low, int high) {
        int value = Integer.parseInt(text());
        if (value < low || value > high) throw new IllegalArgumentException("Integer out of range.");
        return value;
    }
    static double number(double low, double high) {
        double value = Double.parseDouble(text());
        if (!Double.isFinite(value) || value < low || value > high) throw new IllegalArgumentException("Number out of range.");
        return value;
    }
    static void money(String label, double value) { System.out.printf(Locale.ROOT, "%s: %.2f%n", label, value); }
static class Task {
        final int taskId,runTime,priority;
        Task(int id,int time,int priority){taskId=id;runTime=time;this.priority=priority;}
    }
    static final Comparator<Task> ORDER=Comparator.comparingInt((Task t)->t.priority).reversed().thenComparingInt(t->t.taskId);
    public static void main(String[] args){
        try{
            int n=integer(0,50);PriorityQueue<Task> tasks=new PriorityQueue<>(ORDER);Set<Integer> ids=new HashSet<>();
            for(int i=0;i<n;i++){int id=integer(1,1000000),time=integer(1,1000000),priority=integer(0,100);if(!ids.add(id))throw new IllegalArgumentException("Duplicate task ID.");tasks.add(new Task(id,time,priority));}
            System.out.println("Execution order:");long elapsed=0;
            while(!tasks.isEmpty()){Task task=tasks.remove();elapsed+=task.runTime;System.out.println("Task "+task.taskId+" | priority "+task.priority+" | runTime "+task.runTime+" | finishes "+elapsed);}
        }catch(IllegalArgumentException e){System.out.println("Invalid input: "+e.getMessage());}
    }
}

Copy a source file or download the project to preserve its file names.

USE THE RIGHT ENVIRONMENT

How to compile and run

Install a JDK (Java 11 or newer; JDK 17 is suitable), extract this project’s ZIP into its own folder, then open a terminal in that folder.

javac -encoding UTF-8 Main.java
java Main

The standalone source is named Main.java and its entry class is Main. You may also paste it into an online Java compiler and supply the sample input there. Choose Java explicitly; these pages do not change the site compiler or import code automatically.

Each experiment has its own folder because many use the public class Main. Compile and run one experiment at a time.

UNDERSTAND THE JAVA CODE

Line-by-line explanation

These steps follow the exact Java source above. Related statements are grouped; line numbers count blank lines and restart for each file. Start with main to follow execution, then open the constructors, methods or event handlers it uses.

Start with main →

Main.java

Lines 1–4Import utilities and create the shared input reader
import java.util.*;

public class Main {
    private static final Scanner IN = new Scanner(System.in).useLocale(Locale.ROOT);

java.util.* imports utility type names such as Scanner, Locale and collections. Main is the public class matching Main.java. The private static final IN field holds one shared Scanner for System.in; final prevents replacing that reference. The scanner uses Locale.ROOT.

Lines 5–10Read one nonempty trimmed input line
    static String text() {
        if (!IN.hasNextLine()) throw new IllegalArgumentException("Incomplete input.");
        String value = IN.nextLine().trim();
        if (value.isEmpty() || value.length() > 200) throw new IllegalArgumentException("Text must have 1–200 characters.");
        return value;
    }

text checks whether a line exists, reads it and removes leading/trailing whitespace. Reject empty text and lengths above 200 by throwing IllegalArgumentException. Otherwise return the string. One field is read per line, so these helpers do not mix nextInt with leftover newlines.

Lines 11–15Parse and range-check an integer field
    static int integer(int low, int high) {
        int value = Integer.parseInt(text());
        if (value < low || value > high) throw new IllegalArgumentException("Integer out of range.");
        return value;
    }

Integer.parseInt converts the returned string to int or throws NumberFormatException, a subtype of IllegalArgumentException. Reject values outside the caller’s inclusive low/high limits, then return the accepted integer.

Lines 16–20Parse and range-check a finite decimal field
    static double number(double low, double high) {
        double value = Double.parseDouble(text());
        if (!Double.isFinite(value) || value < low || value > high) throw new IllegalArgumentException("Number out of range.");
        return value;
    }

Double.parseDouble reads the string as a double. Double.isFinite rejects NaN and infinities as well as the explicit low/high range check. Return only a finite accepted value; exceptions transfer control to a matching catch block.

Line 21Format a labelled numeric result consistently
    static void money(String label, double value) { System.out.printf(Locale.ROOT, "%s: %.2f%n", label, value); }

money is a formatting helper: %s prints the label, %.2f prints two decimal places, and %n ends the line. Locale.ROOT fixes locale-sensitive formatting. The helper can also display non-currency quantities; it does not change the stored value.

Lines 22–25Store immutable task attributes
static class Task {
        final int taskId,runTime,priority;
        Task(int id,int time,int priority){taskId=id;runTime=time;this.priority=priority;}
    }

Task keeps ID, runtime and priority as final fields. Its constructor assigns all three from the accepted input.

Line 26Order higher priorities first with an ID tie-break
    static final Comparator<Task> ORDER=Comparator.comparingInt((Task t)->t.priority).reversed().thenComparingInt(t->t.taskId);

comparingInt builds priority order; reversed makes larger priority values come first. thenComparingInt chooses smaller taskId when priorities tie. This avoids relying on an unspecified tie order.

Lines 27–30Create the priority queue and reject duplicate task IDs
    public static void main(String[] args){
        try{
            int n=integer(0,50);PriorityQueue<Task> tasks=new PriorityQueue<>(ORDER);Set<Integer> ids=new HashSet<>();
            for(int i=0;i<n;i++){int id=integer(1,1000000),time=integer(1,1000000),priority=integer(0,100);if(!ids.add(id))throw new IllegalArgumentException("Duplicate task ID.");tasks.add(new Task(id,time,priority));}

Read 0–50 tasks and give PriorityQueue the comparator. A HashSet detects repeated IDs. Each accepted task enters the queue as an object reference.

Lines 31–32Remove tasks in comparator order and accumulate finish time
            System.out.println("Execution order:");long elapsed=0;
            while(!tasks.isEmpty()){Task task=tasks.remove();elapsed+=task.runTime;System.out.println("Task "+task.taskId+" | priority "+task.priority+" | runTime "+task.runTime+" | finishes "+elapsed);}

Repeated remove takes the highest-ranked available task. Add its runtime to long elapsed and print its simulated completion time. This is a non-preemptive ordering demonstration, not real operating-system process execution.

Lines 33–35Report invalid input and close the enclosing scopes
        }catch(IllegalArgumentException e){System.out.println("Invalid input: "+e.getMessage());}
    }
}

The catch handles IllegalArgumentException, including invalid numeric strings from the helpers, and prints its message. The final braces close main and Main. Java objects become eligible for garbage collection when no live reference needs them; this program does not manually free them.

FOLLOW THE STATE

Dry run

  1. Task 2 priority 9 runs before tasks 1 and 3 priority 2.
  2. The priority-2 tie chooses task 1 because its ID is smaller.
  3. Runtimes 3,2,5 produce completion totals 3,5,10.
  4. Insertion order does not override the comparator ranking.

CHECK THE BEHAVIOUR

Sample input & output

These outputs were captured by running the exact delivered Java project.

Sample 1

INPUT
3
3
5
2
1
2
2
2
3
9
OUTPUT
Execution order:
Task 2 | priority 9 | runTime 3 | finishes 3
Task 1 | priority 2 | runTime 2 | finishes 5
Task 3 | priority 2 | runTime 5 | finishes 10

Sample 2

INPUT
0
OUTPUT
Execution order:

Common mistakes

  • PriorityQueue iteration is not sorted execution order.
  • Do not use subtraction for comparison when Comparator helpers are clearer and safer.
  • This model is nonpreemptive and assumes all tasks have arrived.

WHY THIS GROWTH RATE?

Time and space complexity

O(n log n) total queue insertions/removals; O(n) stored tasks.

Each of n PriorityQueue insertions and removals can perform O(log n) heap work. Processing all tasks therefore has O(n log n) worst-case queue cost.

Adding runTime and printing one task is fixed numeric work per removal, plus output characters. The simulated clock is long to safely hold the accepted total duration.

These bounds describe the implemented data processing. Input/output text length, scheduling, framework rendering and runtime/library internals are identified separately where they matter. Educational limits bound the actual examples.

PREPARE FOR YOUR LAB VIVA

Related viva questions & answers

6 focused questions

Try answering aloud, then expand to check your reasoning. Questions focus on this program’s classes, state and behavior.

Why is the priority comparator reversed?

Java’s PriorityQueue removes the comparator’s smallest-ranked element. Reversing numeric priority makes the largest task priority rank first for removal.

Why is looping over the queue insufficient to print scheduling order?

PriorityQueue does not guarantee its iterator follows priority order. Repeated removal selects the next highest-ranked task correctly.

How are equal-priority tasks ordered in this example?

They use increasing taskId as a deterministic tie-break. The syllabus leaves ties unspecified, so the page publishes that additional rule.

Does runTime cause the Java thread to sleep?

No. It is simulated execution duration used in printed completion totals. The program’s real running time is not the sum of those durations.

Why is the cumulative completion clock a long?

Many accepted runtimes can be added together. A wider accumulator avoids a narrow integer overflow in that total.

Is this scheduler preemptive?

No. Once a task is selected, its full simulated runtime is accounted for before the next task. Dynamic arrivals and interruption of a running task are outside this exercise.