Unit 2 home
LAB 5 · THREAD SYNCHRONIZATION
2:00:00
LAB 5 · P 1/10PGDN NEXT POINT · PGUP BACK

UI24PC321CS · OOP THROUGH JAVA LAB · B.E. III SEM · VASAVI COLLEGE OF ENGINEERING

Lab 5 — two customers,
one last seat.

A booking counter with a single ticket left and two people clicking at the same instant. You will watch it hand the seat to both of them, then fix it with one word — and prove the fix by running it again.

R-24 SYLLABUS · LAB PROGRAMMING EXERCISE 5 · VERBATIM

“A program to demonstrate Thread Synchronization.”

TOOLEclipse IDESame workspace as Lab 4
GRADED WORK2 exercises100 marks total
WARM-UP5 theory + 4 drillsNot graded · do them first
YOU ALREADY KNOWAll of itClasses 21–24

PART 1 · OPENER · l05-opener

Your bank counter was only safe
because one person used it.

Lab 4's withdraw checked the balance before changing it, and that was enough — because exactly one person was ever at the counter. Put two people there at the same instant and the check stops protecting anything, because both can pass it before either one acts.

ROW H · 8:00 PM · ONE SEAT LEFT

NOW SHOWING · 8:00 PM SCREEN 2 ROW H seatsLeft 1 Diya holds ROW H CONFIRMED Rohit holds ROW H CONFIRMED TOO 1

Watch it once. Then read the code that causes it.

Write your answer in the margin now, in one line. Exercise 1 makes you build it and find out; most people guess wrong.

What is on your screen at 5:20 PM

eclipse-workspace — Lab05 — Eclipse IDE
Lab05
src
com.college.cinema
MovieTicketCounter.java
BookingRequest.java
com.college.app
BoxOffice.java
CONSOLE · BoxOffice [Java Application]
 
— you fill this in at Part 6, twice —
— once broken, once fixed —
TWO PACKAGES, THREE FILES · cinema HOLDS THE SHARED COUNTER AND THE THREAD · app HOLDS ONLY main

Four things you will be able to do

Start a real thread

Two ways — extends Thread and implements Runnable — and know why start() is not run().

Recognise a race

Two threads reading the same field before either writes. You will see a counter lose half its count.

Fix it with a lock

synchronized — one thread inside the method at a time, everyone else waits at the door.

Wait for threads to finish

join() — and handle the InterruptedException it throws, exactly as in Lab 4.

Two hours, ten parts

#PARTMINBY
1Opener20:02
2Prelab theory — five questions, in ink110:13
3Self-mark + the race timeline60:19
4Four drills — five files280:47
5Walkthrough and project setup90:56
6Exercise 1 — the overbooking, 45 marks181:14
7Exercise 1 solution81:22
8Exercise 2 — the fix, 45 marks211:43
9Exercise 2 solution81:51
10Debrief, rubric and viva92:00

WHERE THIS LAB SITS

LAB 5 OF 14 · 10 PARTS · 4 UNGRADED DRILLS + 2 GRADED EXERCISES · 100 MARKS · TWO PACKAGES, THREE FILES

Start the clock in the header now. It runs once for the whole session.

PART 2 · PRELAB THEORY · l05-prelab-theory · 11 MIN

Five questions. Pen, notebook,
before Eclipse opens.

These five are your viva. Real sentences, not keywords — and if one defeats you, write down what you do not know about it.

Q1

What is the difference between a process and a thread?

  • One sentence each.
  • Then the one that matters today: what do two threads of the same program share that two processes do not?
write yours first
A1
  • A process is a running program with its own memory. A thread is one line of execution inside a process.
  • Threads of one program share the same objects on the heap. Processes do not.
  • That sharing is the whole reason threads are fast — and the whole reason today's bug exists.
Q2

Why call start() and not run()?

  • run() compiles and the code inside it does execute — so what exactly is missing?
  • How many threads are alive in each case?
write yours first
A2
  • run() is an ordinary method call — it runs on the thread you are already on.
  • start() asks the JVM for a new thread, which then calls run() for you.
  • Call run() directly and there is still only one thread — the output comes out perfectly in order, and you conclude wrongly that your code is safe.
  • That is the classic mistake: a program that looks correct because it was never actually concurrent.
Q3

What is a race condition, in one sentence a beginner would understand?

  • Then name the two operations that must not be separated in today's booking counter.
write yours first
A3
  • A race condition is when the answer depends on which thread happens to get there first.
  • The two operations: checking seatsLeft > 0 and reducing it.
  • Separate them and a second thread can slip in between — passing a check that is already out of date.
Q4

Trace two threads on an unsynchronized counter.

  • count starts at 0. Each thread runs current = count; then count = current + 1; once.
  • Write the interleaving that ends with count == 1 instead of 2.
write yours first
A4
  • Thread A reads countcurrent = 0.
  • Thread B reads countcurrent = 0 as well. Nothing has been written yet.
  • Thread A writes 0 + 1count = 1.
  • Thread B writes 0 + 1count = 1 again. One increment was silently thrown away.
  • Two increments happened. One survived. That is the whole bug, and Drill 3 shows it 50 times over.
Q5 · DESIGN DECISION

A synchronized method, or a synchronized block?

  • What does each one lock, and for how long?
  • Give one situation where the block is the better choice.
write yours first
A5
  • A synchronized method locks the whole object for the whole method. Simple, and correct for today's exercise.
  • A synchronized block locks a chosen object for only the lines inside the braces.
  • The block wins when only a few lines touch shared data and the rest of the method is slow — reading a file, printing a receipt.
  • Locking longer than necessary is not wrong, it is just slow: everyone else queues at the door for work that never needed the lock.
  • Rule of thumb: start with the method. Move to a block only when you can name what you are speeding up.
Do not reveal a single answer until all five are written. Each unlocks under its own question — the point is finding which of the five you only thought you knew.

PART 3 · SELF-MARK CHECKPOINT · l05-prelab-theory-answers · 6 MIN

One picture that settles
answers 3 and 4.

Each answer unlocked under its own question. Now watch the interleaving on a timeline — the one diagram that makes this lab click.

ONE SEAT, TWO THREADS — READ THE COLUMNS LEFT TO RIGHT

Diya seatsLeft Rohit STEP 1 2 3 4 dashed = read solid = write 1 reads 1 1 reads 1 both have read 1 — neither has written yet writes 0 CONFIRMED 0 writes 0 CONFIRMED TOO 0 Rohit never re-read. He wrote using the value he took at step 2, which step 3 had already replaced.
The fix closes the gap, it does not remove it. synchronized makes Rohit wait at the door until Diya has both read and written.
Check before Part 4. Can you say answers 2 and 3 out loud without reading them? Then close the notebook — the next 28 minutes are typed, not written.

PART 4 · PRELAB CODING TASKS · l05-prelab-coding-tasks · 28 MIN

Four drills. The last two are
the same file, one word apart.

None of these is marked. Build all four in a throwaway project called Lab05Drills, in the default package — no packages until the graded work.

WHAT EACH DRILL REHEARSES

Drill 1 · A thread by extends Thread — and start(), never run()→ EX 1
Drill 2 · The same behaviour by implements Runnable→ EX 1
Drill 3 · Two threads on one counter, no synchronized — watch half the count vanish→ EX 1
Drill 4 · The same file with one word added — the count comes back→ EX 2

FOUR DRILLS · NONE GRADED · DRILL 3 AND DRILL 4 DIFFER BY EXACTLY ONE WORD

DRILL 1A thread of your ownMIRRORS EX 1 · NOT GRADED
PROBLEM

Write Printer that extends Thread. Override run() to print three pages. In main, make two printers, name them Diya and Rohit, and start() both.

INPUT

None. Names are set with the setName method Thread already gives you — no field of your own needed.

EXPECTED OUTPUT

Six lines, three each — but interleaved, and in a different order almost every run.

THE ONE IDEA

If the order changes between runs, you have real concurrency. If it is always Diya's three then Rohit's three, you called run() instead of start().

Run it four or five times. The changing order is the proof, and it is worth seeing before you trust anything else today.
try it first — it is twenty-two lines
DRILL 1 · SOLVED — NO CONSTRUCTOR, NO super, NO FIELD
ECLIPSE SAVES IT ASeclipse-workspace\Lab05Drills\src\Printer.java
Printer.java · PIECE 1 OF 2 — THE PRINTER (lines 1–16)
1public class Printer extends Thread
2{
3 private String owner;
4
5 public void setOwner(String printerOwner)
6 {
7 owner = printerOwner;
8 }
9
10 public void run()
11 {
12 for (int page = 1; page <= 3; page++)
13 {
14 System.out.println(owner + " printing page " + page);
15 }
16 }
Printer.java · PIECE 2 OF 2 — STARTING TWO (lines 18–29)
18 public static void main(String[] args)
19 {
20 Printer p1 = new Printer();
21 Printer p2 = new Printer();
22
23 p1.setOwner("Diya");
24 p2.setOwner("Rohit");
25
26 p1.start();
27 p2.start();
28 }
29}
ECLIPSE CONSOLE · ONE REAL RUN
Diya printing page 1
Rohit printing page 1
Rohit printing page 2
Rohit printing page 3
Diya printing page 2
Diya printing page 3
Your order will differ — that is correct, not a mistake. Diya started first and Rohit still finished first, because the JVM decides who runs when, and nobody can promise otherwise.
  • line 10run() is the work. You never call it yourself — lines 26 and 27 call start(), and each new thread calls run() for you.
  • line 3One plain field, set by setOwner on line 5. No constructor — exactly the pattern you used for open() in Lab 4.
  • no superNothing here needs super. Lab 4's two super(message) calls remain the only ones in this deck that had no alternative.
  • DRILL 2The same job, the other wayMIRRORS EX 1 · NOT GRADED
    PROBLEM

    Write PrintJob that implements Runnable. Same three pages. This time the owner's name is your own field, set by setOwner — because Runnable is not a Thread and has no name to inherit.

    INPUT

    None.

    EXPECTED OUTPUT

    Identical in shape to Drill 1 — six interleaved lines.

    THE ONE IDEA

    Runnable is the job; Thread is the worker. You hand the job to a worker with new Thread(job).start().

    WHY THIS SECOND WAY EXISTS AT ALL

    Java allows only one parent class. Spend it on Thread and your class can never extend anything else. implements Runnable leaves that slot free, which is why most real code prefers it.

    same output, different shape
    DRILL 2 · SOLVED — THE JOB AND THE WORKER, SEPARATED
    ECLIPSE SAVES IT ASeclipse-workspace\Lab05Drills\src\PrintJob.java
    PrintJob.java · PIECE 1 OF 2 — THE JOB (lines 1–16)
    1public class PrintJob implements Runnable
    2{
    3 private String owner;
    4
    5 public void setOwner(String jobOwner)
    6 {
    7 owner = jobOwner;
    8 }
    9
    10 public void run()
    11 {
    12 for (int page = 1; page <= 3; page++)
    13 {
    14 System.out.println(owner + " printing page " + page);
    15 }
    16 }
    LINE 1 IS THE WHOLE DIFFERENCE
    implements Runnable gives you one duty: write run(). It gives you nothing else — no start(), no getName(), no thread. Lines 26 and 27 are where that shows: the job has to be handed to a Thread before anything can run.
    PrintJob.java · PIECE 2 OF 2 — STARTING IT (lines 18–29)
    18 public static void main(String[] args)
    19 {
    20 PrintJob j1 = new PrintJob();
    21 PrintJob j2 = new PrintJob();
    22
    23 j1.setOwner("Diya");
    24 j2.setOwner("Rohit");
    25
    26 new Thread(j1).start();
    27 new Thread(j2).start();
    28 }
    29}
    ECLIPSE CONSOLE · ONE REAL RUN
    Rohit printing page 1
    Rohit printing page 2
    Rohit printing page 3
    Diya printing page 1
    Diya printing page 2
    Diya printing page 3
    Lines 26 and 27 are the hand-off. new Thread(j1) is Java's own constructor — one of the few you will type today, and only because the API asks for the job up front.
    DRILL 3Watch a counter lose half its countTHE IMPORTANT ONE

    This drill is the heart of the lab. Everything before it was two threads politely printing. Here they touch the same object.

    PROBLEM

    Two files. Counter holds a private count and an increment() that reads into a local, pauses, then writes back. Bumper extends Thread takes a shared counter through setUp and increments it 50 times. In main, two Bumpers share one Counter, then join() both and print the total.

    INPUT

    None. Two threads × 50 increments each.

    EXPECTED OUTPUT

    Expected 100, got 50

    And it is 50 every single run — not a flicker, a collapse.

    THE ONE IDEA

    Every increment read a value that another thread was about to overwrite. A hundred increments happened; fifty survived.

    WHY THE SLEEP IS IN THERE, AND WHY IT IS HONEST

    The Thread.sleep(2) between the read and the write does not create the bug — it widens a gap that is always there. Without it the gap is a few nanoseconds and you might get 100 on a lucky run, conclude the code is fine, and ship it. Real systems hit that gap under load; the sleep just lets you see it on a quiet lab machine.

    run it three times first — write the numbers down
    DRILL 3 · SOLVED — TWO FILES, ONE SHARED OBJECT
    FILE 1 OF 2 · ECLIPSE SAVES IT ASeclipse-workspace\Lab05Drills\src\Counter.java
    Counter.java
    1public class Counter
    2{
    3 private int count;
    4
    5 public void increment()
    6 {
    7 int current = count;
    8 pause();
    9 count = current + 1;
    10 }
    11
    12 public int getCount()
    13 {
    14 return count;
    15 }
    16
    17 private void pause()
    18 {
    19 try
    20 {
    21 Thread.sleep(2);
    22 }
    23 catch (InterruptedException e)
    24 {
    25 System.out.println("Interrupted mid-count");
    26 }
    27 }
    28}
    LINES 7 AND 9 ARE THE BUG
    Line 7 reads. Line 9 writes. Line 8 is the gap any other thread can walk into. Written as count = count + 1 on one line the gap is still there — the JVM still reads, adds, then writes. This version just makes it visible.
    FILE 2 OF 2 · ECLIPSE SAVES IT ASeclipse-workspace\Lab05Drills\src\Bumper.java
    Bumper.java · PIECE 1 OF 2 — THE THREAD (lines 1–16)
    1public class Bumper extends Thread
    2{
    3 private Counter shared;
    4
    5 public void setUp(Counter sharedCounter)
    6 {
    7 shared = sharedCounter;
    8 }
    9
    10 public void run()
    11 {
    12 for (int i = 1; i <= 50; i++)
    13 {
    14 shared.increment();
    15 }
    16 }
    Bumper.java · PIECE 2 OF 2 — MAIN (lines 18–42)
    18 public static void main(String[] args)
    19 {
    20 Counter tally = new Counter();
    21
    22 Bumper a = new Bumper();
    23 Bumper b = new Bumper();
    24 a.setUp(tally);
    25 b.setUp(tally);
    26
    27 a.start();
    28 b.start();
    29
    30 try
    31 {
    32 a.join();
    33 b.join();
    34 }
    35 catch (InterruptedException e)
    36 {
    37 System.out.println("Interrupted while waiting");
    38 }
    39
    40 System.out.println("Expected 100, got " + tally.getCount());
    41 }
    42}
    ECLIPSE CONSOLE · FIVE REAL RUNS
    Expected 100, got 50
    Expected 100, got 50
    Expected 100, got 50
    Expected 100, got 50
    Expected 100, got 50
    Lines 24 and 25 are why this happens at all. Both Bumpers were handed the same tally object — one Counter, two threads. Give each its own and the bug disappears along with the point.
  • line 24setUp, not a constructor. The counter is shared by reference — both threads hold the same object, which is the entire mechanism.
  • line 32join() means wait here until that thread is finished. Without it, line 41 prints before the threads have done any work at all.
  • line 35join() throws a checked InterruptedException — so it needs the try/catch you wrote four times in Lab 4. This is the handoff Lab 4 promised you.
  • DRILL 4Fix it with one wordMIRRORS EX 2 · NOT GRADED
    PROBLEM

    Do not write a new file. Open Counter.java and add synchronized to the increment method — between public and void. Change nothing else. Save, run.

    INPUT

    None. Identical to Drill 3.

    EXPECTED OUTPUT

    Expected 100, got 100

    Every run, without exception.

    THE ONE IDEA

    One thread inside increment at a time. The second waits at the door until the first has read and written. The gap still exists; nobody can get into it.

    Notice what did not change. The sleep is still there. The threads are still two. The only difference is that the read and the write can no longer be separated — and that is the entire syllabus line for today.
    it really is one word
    DRILL 4 · SOLVED — THE ENTIRE DIFF
    Counter.java · LINE 5 ONLY
    5 public void increment() // before
    5 public synchronized void increment() // after
    ECLIPSE CONSOLE · THREE REAL RUNS
    Expected 100, got 100
    Expected 100, got 100
    Expected 100, got 100
    Fifty lost increments came back for one word. That word is the answer to the exam question, and you have now watched it work rather than been told it does.
    What it locks: the Counter object itself. Both threads share one Counter, so both queue for the same lock — which is exactly why it works.
    All four drills done. You can create a thread two ways, spot a race, and close it. Parts 6 and 8 are the same two ideas at booking-counter scale — nothing new is coming, only bigger.

    PART 5 · WALKTHROUGH · l05-walkthrough · 9 MIN

    The graded project.
    Two packages, built once.

    Drills lived in the default package. The graded work does not — the habit from Lab 3 holds. This is the setup you do once, so Parts 6 and 8 are about threads and never about folders.

    1

    File › New › Java Project — named Lab05

    A second project; leave Lab05Drills alone. Untick Create module-info.java if offered.

    2

    Right-click src › New › Package — twice

    Type the full dotted name each time. Never build com, then college, then cinema as three separate packages.

    New Java Package

    Java Package

    Create a new Java package.

    Lab05/src
    com.college.cinema
    Create package-info.java
    CancelFinish
    REPEAT FOR com.college.app · TWO PACKAGES, ONE DOTTED NAME EACH, NEVER TYPED IN PIECES
    3

    Right-click the cinema package › New › Class

    Name it MovieTicketCounter. Check that Package already reads com.college.cinema before you press Finish — if it is blank, you right-clicked src instead.

    New Java Class

    Java Class

    Create a new Java class.

    Lab05/src
    com.college.cinema
    MovieTicketCounter
    public static void main(String[] args)
    CancelFinish
    LEAVE main UNTICKED FOR THE FIRST TWO FILES · ONLY BoxOffice GETS IT
    File Explorer — what the two wizard runs created
    C:\Users\student\eclipse-workspace\Lab05\src
    com
    college
    cinema
    app
    FOUR REAL FOLDERS · cinema AND app ARE SIBLINGS INSIDE college — NEITHER IS NESTED IN THE OTHER

    CHECK THIS NOW, NOT AT 3:50 PM

    Package Explorer › three dots › Package PresentationHierarchical. Two siblings under college is correct; one nested inside the other is not. Ten marks ride on this tree alone, before your logic is read.

    The order you write the three files in

    This order is not a suggestion. Each file compiles only once the one above it exists.

    THREE FILES · IN THIS SEQUENCE

    1 · com.college.cinema.MovieTicketCounter — the shared objectEX 1
    2 · com.college.cinema.BookingRequest — the thread that uses itEX 1
    3 · com.college.app.BoxOffice — the program that starts bothEX 1

    EXERCISE 2 ADDS NO FILES — IT ADDS ONE WORD TO FILE 1

    If you fall behind: every drill solution is in Part 4, and the graded solutions in Parts 7 and 9. Get it compiling, then retype it from memory.

    PART 6 · GRADED · l05-exercise-1-ticket-counter-unsafe · 18 MIN

    Exercise 1 · Build the counter
    that sells one seat twice.

    Three files, forty-five marks. You are being marked on writing a program that demonstrates the bug — a correct-looking booking counter that overbooks under two threads. No code is given here.

    EXERCISE 1MovieTicketCounter + BookingRequest + BoxOffice45 MARKS
    PROBLEM

    File 1 — com.college.cinema.MovieTicketCounter. A private seatsLeft. An open(int seats). A bookTicket(String customer) that prints what the customer sees, pauses, reduces the count, prints CONFIRMED — or prints SOLD OUT if nothing is left. A private pause() holding the sleep and its try/catch.

    File 2 — BookingRequest extends Thread. Holds a counter. A setUp(counter, name) that stores it and calls the inherited setName. A run() that books one ticket.

    File 3 — com.college.app.BoxOffice. One counter opened with 1 seat, two requests — Diya and Rohit — sharing it, both started.

    INPUT

    None typed. Hard-coded: 1 seat, two customers.

    The evaluator will run it several times.

    EXPECTED OUTPUT

    Diya sees 1 seat left
    Rohit sees 1 seat left
    Diya CONFIRMED
    Rohit CONFIRMED

    Which name appears first will vary. What must not vary is that both say CONFIRMED.

    THE ONE IDEA

    Both threads must be handed the same counter object. Two new MovieTicketCounter() calls and each thread gets its own seat — the program runs, prints the same four lines, and demonstrates nothing.

    THE FOUR RULES THIS EXERCISE IS MARKED ON

    • One counter, shared. Created once in BoxOffice, passed to both requests through setUp.
    • start(), never run(). Call run() and the output is always Diya-then-Rohit with no overbooking — a program that quietly proves nothing.
    • The pause sits between the read and the write. Move it above the if and the bug stops reproducing.
    • No constructor, no static field. setUp and open do the work; the counter must be a real object shared by reference, which a static field would hide.
    Run it five times before you call it done. If any run shows only one CONFIRMED, your threads are not overlapping — check that you called start() and that the pause is inside the if.

    PART 7 · SOLUTION & OUTPUT · l05-exercise-1-solution-output · 8 MIN

    Exercise 1, solved — and
    the overbooking on screen.

    Different names and wording are fine. A shared object that is not actually shared is not — that is what the marks are for.

    only once you have run yours five times
    EXERCISE 1 · SOLVED — THE BUG, REPRODUCED ON PURPOSE
    FILE 1 OF 3 · ECLIPSE SAVES IT ASeclipse-workspace\Lab05\src\com\college\cinema\MovieTicketCounter.java
    MovieTicketCounter.java · PIECE 1 OF 2 — THE BOOKING RULE (lines 1–25)
    1package com.college.cinema;
    2
    3public class MovieTicketCounter
    4{
    5 private int seatsLeft;
    6
    7 public void open(int seats)
    8 {
    9 seatsLeft = seats;
    10 }
    11
    12 public void bookTicket(String customer)
    13 {
    14 if (seatsLeft > 0)
    15 {
    16 System.out.println(customer + " sees " + seatsLeft + " seat left");
    17 pause();
    18 seatsLeft = seatsLeft - 1;
    19 System.out.println(customer + " CONFIRMED");
    20 }
    21 else
    22 {
    23 System.out.println(customer + " SOLD OUT");
    24 }
    25 }
    LINE 14 CHECKS. LINE 18 CHANGES.
    Four lines apart, and a whole other thread fits between them. Line 14 asks is there a seat?, line 18 takes it — and line 17's pause is the window where Rohit walks in and asks the same question, getting the same yes.
    MovieTicketCounter.java · PIECE 2 OF 2 — THE PAUSE (lines 27–38)
    27 private void pause()
    28 {
    29 try
    30 {
    31 Thread.sleep(100);
    32 }
    33 catch (InterruptedException e)
    34 {
    35 System.out.println("Booking interrupted");
    36 }
    37 }
    38}
    WHY pause() IS ITS OWN METHOD
    It keeps bookTicket readable. The try/catch around sleep is four lines of noise that has nothing to do with booking — and private says plainly that nobody outside this class should call it.
    FILE 2 OF 3 · ECLIPSE SAVES IT ASeclipse-workspace\Lab05\src\com\college\cinema\BookingRequest.java
    BookingRequest.java
    1package com.college.cinema;
    2
    3public class BookingRequest extends Thread
    4{
    5 private MovieTicketCounter counter;
    6 private String customer;
    7
    8 public void setUp(MovieTicketCounter sharedCounter, String customerName)
    9 {
    10 counter = sharedCounter;
    11 customer = customerName;
    12 }
    13
    14 public void run()
    15 {
    16 counter.bookTicket(customer);
    17 }
    18}
    EIGHTEEN LINES, NO CONSTRUCTOR
    Lines 10 and 11 store the two things this thread needs: the shared counter, and who is asking. No constructor and no supersetUp does the job, exactly as open() did in Lab 4. Line 16 is the whole of run().
    FILE 3 OF 3 · ECLIPSE SAVES IT ASeclipse-workspace\Lab05\src\com\college\app\BoxOffice.java
    BoxOffice.java
    1package com.college.app;
    2
    3import com.college.cinema.BookingRequest;
    4import com.college.cinema.MovieTicketCounter;
    5
    6public class BoxOffice
    7{
    8 public static void main(String[] args)
    9 {
    10 MovieTicketCounter counter = new MovieTicketCounter();
    11 counter.open(1);
    12
    13 BookingRequest diya = new BookingRequest();
    14 BookingRequest rohit = new BookingRequest();
    15
    16 diya.setUp(counter, "Diya");
    17 rohit.setUp(counter, "Rohit");
    18
    19 diya.start();
    20 rohit.start();
    21 }
    22}
    ECLIPSE CONSOLE · THREE REAL RUNS
    run 1
    Rohit sees 1 seat left
    Diya sees 1 seat left
    Diya CONFIRMED
    Rohit CONFIRMED
    run 2
    Diya sees 1 seat left
    Rohit sees 1 seat left
    Rohit CONFIRMED
    Diya CONFIRMED
    The order swaps; the overbooking does not. Line 11 opened the counter with one seat, and two people walked away holding it.
  • line 11counter.open(1)one seat. The whole demonstration depends on that number being smaller than the number of threads.
  • lines 16–17Both setUp calls pass the same counter variable. This single fact is what makes them threads-in-conflict rather than two unrelated programs.
  • lines 19–20start(), twice. Swap either for run() and the output becomes tidy, correct, and worthless as a demonstration.
  • PART 8 · GRADED · l05-exercise-2-ticket-counter-safe · 21 MIN

    Exercise 2 · Fix it — and prove
    the fix with a second run.

    Forty-five marks, and no new file. The marks are not for typing the word; they are for proving it worked and being able to say what it locks.

    EXERCISE 2synchronized bookTicket — and the evidence45 MARKS
    PROBLEM

    Step 1. In MovieTicketCounter, mark bookTicket as synchronized. Change nothing else — same pause, same threads, same one seat.

    Step 2. Run it five times and record every run in your notebook.

    Step 3. Write two sentences: what object is locked, and why locking it is enough when two different threads are involved.

    INPUT

    Identical to Exercise 1. That is the point — only the lock changed.

    EXPECTED OUTPUT

    Diya sees 1 seat left
    Diya CONFIRMED
    Rohit SOLD OUT

    Three lines, not four. Rohit never sees a seat, because by the time he is let in there is none.

    THE ONE IDEA

    The lock is on the counter object, not on the method.

    • Both threads call that same object's method, so both queue for one lock.
    • Two separate counters would mean two separate locks — and no protection at all.

    THE THREE RULES THIS EXERCISE IS MARKED ON

    • The word is on bookTicket, not on run(). Synchronizing run() locks the BookingRequest — and the two requests are different objects, so it protects nothing. This is the commonest wrong answer in the exam.
    • Five recorded runs. One run proves nothing about a race; the evaluator will ask how many you did.
    • Nothing else changed. Same pause, same seat count. A fix that also removes the sleep has not demonstrated anything.
    Before you run it, predict the third line. Write down whether Rohit will say SOLD OUT or nothing at all, and why. Getting this wrong on paper and right on screen is the most useful thing that can happen in this lab.

    PART 9 · SOLUTION & OUTPUT · l05-exercise-2-solution-output · 8 MIN

    Exercise 2, solved — one word,
    and five identical runs.

    did you write your prediction down?
    EXERCISE 2 · SOLVED — THE ENTIRE DIFF IS ONE WORD
    MovieTicketCounter.java · LINE 12 ONLY
    12 public void bookTicket(String customer) // Exercise 1
    12 public synchronized void bookTicket(String customer) // Exercise 2
    ECLIPSE CONSOLE · FIVE REAL RUNS
    Diya sees 1 seat left
    Diya CONFIRMED
    Rohit SOLD OUT
    — and identically on runs 2, 3, 4 and 5 —
    Three lines, and the same three every time. Exercise 1 could not promise you an order; Exercise 2 can promise you an outcome — one seat, one confirmation.
  • the lockMarked on an instance method, synchronized locks the object the method was called on — here, the one shared MovieTicketCounter.
  • why enoughBoth threads call counter.bookTicket(...) on that same object, so they contend for one lock. Rohit is held at line 12 until Diya leaves line 25.
  • the sleepStill 100 ms, still between the check and the change. The gap was never removed — it was made impossible to enter.
  • SOLD OUTRohit does reach bookTicket, and does run the else. He is not blocked forever — just made to wait his turn, then told the truth.
  • Try it with 2 seats and 3 customers. Two CONFIRMED, one SOLD OUT — every run. That is the generalisation the viva may ask for.

    PART 10 · DEBRIEF · l05-common-mistakes-debrief-checkpoint · 9 MIN

    Five real mistakes, the rubric,
    and what to show the evaluator.

    Threads are unusual: most of these compile and run perfectly and still score zero, because the program no longer demonstrates anything.

    #WHAT YOU DIDWHAT YOU SEEWHY IT COSTS MARKS
    1Called run() instead of start()Diya's lines, then Rohit's — perfectly in orderNo second thread was ever created. The output looks correct because nothing was concurrent.
    2Gave each thread its own counterDiya CONFIRMED · Rohit CONFIRMEDLooks like the bug, but is not. Two seats existed. Nothing was shared, so nothing raced.
    3Put synchronized on run()still both CONFIRMEDLocks the BookingRequest, and the two requests are different objects — two locks, no protection.
    4Moved the pause above the ifusually one CONFIRMEDThe gap between check and change closes, so the bug stops reproducing and Exercise 1 proves nothing.
    5Forgot join() in Drill 3Expected 100, got 0Main printed the total before the threads did any work. Nothing to do with synchronization.
    Mistakes 1 and 2 are the expensive ones — both produce plausible output, so students stop checking. Ask of every run: was anything actually shared, and was anything actually concurrent?

    The rubric — 100 marks

    #WHAT IS ASSESSEDDETAILMARKS
    1Two packagescom.college.cinema and com.college.app as siblings, in the package line and on disk.10
    2Ex 1 · the shared counterOne MovieTicketCounter, opened with 1 seat, passed to both requests.15
    3Ex 1 · a real threadextends Thread, run() overridden, start() called. No constructor, no static field.10
    4Ex 1 · the bug reproducesPause between check and change; both CONFIRMED on a live run.10
    5Ex 2 · synchronized on bookTicketOn the counter's method — not on run().20
    6Ex 2 · the evidenceFive runs recorded, all showing one CONFIRMED and one SOLD OUT.15
    7Ex 2 · explain the lockTwo sentences: what object is locked, why that is enough.10
    8VivaTwo or three from Part 2 — process vs thread, start() vs run(), race conditions, method vs block.10
    TOTALExercise 1 · 45  +  Exercise 2 · 45  +  Viva · 10100

    THE TWENTY-FIVE MARKS THAT NEED NO LUCK

    Rows 1 and 3 total 20 marks for structure alone — two packages and a properly started thread, both visible before anything runs. Row 5's twenty go for one word in the right place.

    What to show the evaluator

    1

    Package Explorer, Hierarchical

    Lab05 › src › com › college with cinema and app as siblings, three files, no red markers.

    2

    MovieTicketCounter.java open at bookTicket

    Lines 12 to 25 on screen so the synchronized keyword, the check and the change are visible together. Expect the question "what does that lock?" — twenty marks, answered by pointing at one line.

    3

    Two live runs, back to back

    Comment synchronized out, run — both CONFIRMED. Put it back, run — one CONFIRMED, one SOLD OUT. Doing this twice in front of them is the exercise.

    4

    Your notebook

    Five theory answers in ink, plus the five recorded runs from Exercise 2. The viva is drawn from these.

    Your folder after this lab

    File Explorer — what is on your disk when you walk out
    C:\Users\student\eclipse-workspace\Lab05\src
    com
    college
    cinema
    MovieTicketCounter.java
    BookingRequest.java
    app
    BoxOffice.java
    TWO PACKAGES, THREE FILES · PLUS Lab05Drills WITH FOUR DRILL FILES · UNIT 2 IS NOW COMPLETE

    LAB 5 · DONE · SYLLABUS PROGRAMMING EXERCISE 5 CLOSED · UNIT 2 COMPLETE

    One seat, two customers,
    and one word that settles it.

    You created threads both ways, shared a single object between them, and watched a correct-looking booking counter hand the same seat to two people. Then you closed it with synchronized and proved the fix across five runs. The syllabus said “demonstrate Thread Synchronization” — and you demonstrated the break before the fix, which is the only way the fix means anything.

    Four drills got you there: a thread by extends Thread, the same job by implements Runnable, a counter that lost half its count, and the one word that gave it back. join() and its InterruptedException came straight from Lab 4 — the handoff that lab promised you.

    Unit 2 is now complete. Classes 13 to 24 and Labs 3, 4 and 5: packages, exceptions, threads. Every one of them was about the same instinct — what happens when something goes wrong, and does your program tell the truth about it?

    [FWD → Unit 3] — strings, I/O streams and serialization, starting with Lab 6 · the String classes. Keep both Lab05 projects: the lock you learned today comes back the moment two things touch one collection, in Unit 4.