Skip to lab content

WEEK 7 · OBJECT ORIENTED PROGRAMMING LAB

Alternating odd and even threads

EXPERIMENT 14Java 11+Console program

THE SYLLABUS TASK

What you need to build

Create two threads, where:
Full syllabus requirements: classes, fields and methods
Create two threads, where: • Thread-A prints even numbers up to 10 Thread-B prints odd numbers up to 10 Both must run concurrently and display results alternately. (Use sleep() to visualize the threads execution)
Input format & conventions

No input. Thread-B prints odd values 1–9 and Thread-A prints even values 2–10. Output is ordered 1 through 10. sleep(10) visualizes execution outside the shared monitor.

UNDERSTAND THE IDEA

Explanation

Two threads share an Alternator holding the next value. A synchronized method waits while the current value belongs to the other thread, prints it, advances it and wakes waiters.

wait uses a while condition so waking does not imply the turn is now valid. sleep occurs after leaving the synchronized method; it adds delay but does not enforce order. Shared cancellation prevents interrupted workers from stranding their partner.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Start both workers using start().
  2. Wait until the next value has the worker’s parity.
  3. Print and increment under the same monitor.
  4. Wake the other worker and sleep briefly outside the lock.
  5. Exit after 10 and join both workers.

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.

Guarded odd/even turn

Guarded odd/even turn

Open full-size flowchart ↗

Java 11+

Complete Java source

Main.java

Download .java
public class Main {
    static class Alternator {
        private int next=1; private boolean cancelled;
        synchronized boolean printTurn(int parity,String name) throws InterruptedException {
            while(!cancelled && next<=10 && next%2!=parity)wait();
            if(cancelled || next>10){notifyAll();return false;}
            System.out.println(name+": "+next++);notifyAll();return true;
        }
        synchronized void cancel(){cancelled=true;notifyAll();}
    }
    static class NumberThread extends Thread {
        private final Alternator state; private final int parity;
        NumberThread(Alternator state,int parity,String name){super(name);this.state=state;this.parity=parity;}
        @Override public void run(){
            try {while(state.printTurn(parity,getName()))Thread.sleep(10);}
            catch(InterruptedException e){state.cancel();Thread.currentThread().interrupt();}
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Alternator state=new Alternator();Thread a=new NumberThread(state,0,"Thread-A"),b=new NumberThread(state,1,"Thread-B");
        a.start();b.start();
        try{a.join();b.join();}catch(InterruptedException e){state.cancel();a.interrupt();b.interrupt();throw e;}
    }
}

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–3Keep the next number and cancellation flag in one shared object
public class Main {
    static class Alternator {
        private int next=1; private boolean cancelled;

Main contains Alternator, whose next field starts at 1. Both worker threads receive the same Alternator reference, so they coordinate one number sequence rather than two independent counters.

Lines 4–5Wait for the thread’s parity while holding the monitor
        synchronized boolean printTurn(int parity,String name) throws InterruptedException {
            while(!cancelled && next<=10 && next%2!=parity)wait();

synchronized permits one caller at a time on this Alternator. wait releases that monitor while waiting and reacquires it before returning. A while condition rechecks whose turn it is after every wake-up.

Lines 6–8Stop when finished or print one number and notify waiters
            if(cancelled || next>10){notifyAll();return false;}
            System.out.println(name+": "+next++);notifyAll();return true;
        }

If cancelled or past 10, wake other waiters and return false. Otherwise print next, increment it, notifyAll and return true. The increment happens inside the synchronized operation, so the two workers cannot print the same number.

Lines 9–10Provide a cancellation path that wakes blocked threads
        synchronized void cancel(){cancelled=true;notifyAll();}
    }

cancel records the stop flag and notifies waiters while using the same monitor. This lets the other worker finish rather than remain asleep indefinitely.

Lines 11–14Configure a named worker with its shared state and parity
    static class NumberThread extends Thread {
        private final Alternator state; private final int parity;
        NumberThread(Alternator state,int parity,String name){super(name);this.state=state;this.parity=parity;}
        @Override public void run(){

NumberThread extends Thread. Its constructor supplies the thread name to super and saves the shared Alternator and parity. run is the work executed after start creates a running thread.

Lines 15–18Repeat turns and preserve interruption status
            try {while(state.printTurn(parity,getName()))Thread.sleep(10);}
            catch(InterruptedException e){state.cancel();Thread.currentThread().interrupt();}
        }
    }

While printTurn returns true, sleep briefly between calls. sleep occurs after the synchronized call has returned; it is not what enforces alternation. On interruption cancel the shared work and restore the current thread’s interruption flag.

Lines 19–21Create both workers and start concurrent execution
    public static void main(String[] args) throws InterruptedException {
        Alternator state=new Alternator();Thread a=new NumberThread(state,0,"Thread-A"),b=new NumberThread(state,1,"Thread-B");
        a.start();b.start();

Thread-A has parity 0 and therefore prints even values. Thread-B has parity 1 and prints the first value 1. Calling start, rather than run directly, enables the two workers to execute concurrently.

Lines 22–24Wait for both workers and cancel safely if main is interrupted
        try{a.join();b.join();}catch(InterruptedException e){state.cancel();a.interrupt();b.interrupt();throw e;}
    }
}

join waits for completion. If main’s wait is interrupted, cancel shared state, interrupt both workers and rethrow. The final braces close main and Main.

FOLLOW THE STATE

Dry run

  1. next=1 makes the even worker wait.
  2. The odd worker prints 1, advances to 2 and notifies.
  3. The even worker prints 2 and returns the turn.
  4. The guarded sequence repeats until 10, regardless of which thread starts first.

CHECK THE BEHAVIOUR

Sample input & output

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

Sample 1

INPUT
(no input)
OUTPUT
Thread-B: 1
Thread-A: 2
Thread-B: 3
Thread-A: 4
Thread-B: 5
Thread-A: 6
Thread-B: 7
Thread-A: 8
Thread-B: 9
Thread-A: 10

Common mistakes

  • sleep alone cannot guarantee alternate printing.
  • Call start(), not run(), to create concurrent execution.
  • Recheck turn conditions after wait returns.

WHY THIS GROWTH RATE?

Time and space complexity

O(k) prints for k values; O(1) shared state; scheduling and sleep affect wall-clock time.

Each accepted value is printed and incremented once. Generalizing the upper limit to k gives O(k) successful steps, while this exercise fixes k=10.

Waits depend on scheduling, and the intentional sleep adds elapsed delay. These are distinct from the count of useful algorithm steps. Two threads and fixed shared fields require constant application state.

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 sleep not enough to guarantee alternation?

It delays a thread but does not transfer a protected turn to another thread. Scheduling can still run one thread repeatedly unless shared state enforces the order.

Why is next checked inside a synchronized method?

The parity check, output and increment must be one protected sequence. Otherwise both threads could observe or change the value inconsistently.

Why does the wait guard use while instead of if?

A wakeup only permits the thread to recheck its condition. Spurious wakeups or another state change can leave it still without the correct turn.

Why is sleep placed outside the monitor?

sleep does not release a held monitor. Sleeping inside the synchronized region would unnecessarily block the partner from taking its valid turn.

Why does the odd thread print first in this implementation?

The shared next value starts at 1. The even worker waits until next becomes even, so initialization defines the order rather than startup timing.

What is the purpose of join in the driver?

It waits for both workers to finish before main completes its coordination. It does not itself impose the odd/even turn order.