Java Level 4 Test
One cashier serving one queue is easy to understand. Four cashiers sharing one cash drawer is where the trouble starts: two people reach for the same note, someone waits for a drawer that never opens, and the till is short at the end of the day. Most of this level of the course was about that second shop: threads, locks, executors and virtual threads, plus the JVM underneath and the newer language features. This test checks that you can spot the trouble before it happens.
How the Test Works
- What's in it: 20 multiple-choice questions, then 3 puzzles where you work out what a short program prints (and, once, how to fix it).
- Time: 30 minutes, without notes, an IDE or JShell.
- Pass mark: 16 out of 23. The quiz marks the 20 questions for you. Mark the puzzles yourself, one point each, and add them to your quiz score.
- What it covers: the lessons from "Multithreading in Java" to the newest platform features.
Part 1: 20 Questions
Part 2: Output Puzzles
Work out each answer on paper before you open the solution. The solution shows a full program, its real output and a Try it button. Give yourself one point for each puzzle you get right.
Puzzle 1: Calling Run Before Start
What is printed?
Thread t = new Thread(() -> System.out.print("A"));
t.run();
System.out.print("B");
t.start();
t.join();
System.out.print("C");Show solution
public class RunStartPuzzle {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> System.out.print("A"));
t.run();
System.out.print("B");
t.start();
t.join();
System.out.print("C");
System.out.println();
}
}ABACWhy this output?
t.run() is a normal method call, so the main thread prints the first A. Then B. t.start() runs the same code in a new thread, which prints the second A, and join() waits for it to finish before C is printed. So the order is always the same. Revise: Calling Thread.run() Directly vs Thread.start().
Puzzle 2: Two Threads, One Counter
What is printed, and how do you fix it?
int[] count = {0};
Runnable task = () -> {
for (int i = 0; i < 10_000; i++) count[0]++;
};
Thread t1 = new Thread(task), t2 = new Thread(task);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(count[0]);The answer to this one is not a single number, so the solution shows the fixed program instead.
Show solution
import java.util.concurrent.atomic.AtomicInteger;
public class CounterFixed {
public static void main(String[] args) throws InterruptedException {
AtomicInteger count = new AtomicInteger();
Runnable task = () -> {
for (int i = 0; i < 10_000; i++) {
count.incrementAndGet();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count.get());
}
}20000Why this output?
The original usually prints less than 20000, and a different number on each run. count[0]++ is three steps: read, add one, write. When both threads read the same value, one update is lost. Give yourself the point if you said "less than 20000, it varies" and named a fix: an AtomicInteger with incrementAndGet(), as above, or a synchronized block around the increment. Revise: Race Conditions and Thread Safety.
Puzzle 3: A Sealed Switch
What is printed?
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
static String describe(Shape s) {
return switch (s) {
case Circle c when c.r() > 10 -> "big circle";
case Circle c -> "circle";
case Square(double side) -> "square " + side;
};
}
// in main:
System.out.println(describe(new Circle(5)) + ", "
+ describe(new Square(2)));Show solution
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
public class ShapePuzzle {
static String describe(Shape s) {
return switch (s) {
case Circle c when c.r() > 10 -> "big circle";
case Circle c -> "circle";
case Square(double side) -> "square " + side;
};
}
public static void main(String[] args) {
System.out.println(describe(new Circle(5)) + ", "
+ describe(new Square(2)));
}
}circle, square 2.0Why this output?
A radius of 5 fails the guard c.r() > 10, so the next case, plain Circle c, matches. For the square, the record pattern pulls out side as a double, so 2 prints as 2.0. No default is needed, because the interface is sealed and both types are covered. Revise: Guards in Switch (When Clause).
What Your Score Means
| Score (out of 23) | What it means | What to do next |
|---|---|---|
| 20 to 23 | You are ready for real-world Java | Move on to Build Tools in Java: Maven and Gradle |
| 16 to 19 | A pass, with a few gaps | Read the lessons linked from the questions you missed, then move on |
| Under 16 | Not yet | Reread the lessons behind your wrong answers, especially the concurrency ones, and take the test again |
Was this tutorial helpful?