ExerciseChallenge
Challenge
Five singletons, rank them
20 minintermediate1–10 yrs
Edge cases. You have to reason, and two valid fixes differ.
What this teaches
- Each form differs on laziness, locking, reflection and serialization
- Double-checked locking without volatile is broken, not merely risky
- Only the enum closes all three doors with no code from you
- A correctly constructed singleton can still hold shared mutable state
Starter
Starter.java
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
/**
* Challenge: five singletons. Score them, then rank them.
*
* Four axes: lazy, thread-safe, survives reflection, survives serialization.
* Fill the table in before writing any code — three of the five can be
* decided by reading, and one of the defects cannot be demonstrated by
* running it at all.
*/
public class Starter {
/* ─────────── A ─────────── */
static class Eager implements Serializable {
private static final Eager INSTANCE = new Eager();
private Eager() { }
static Eager get() { return INSTANCE; }
}
/* ─────────── B ─────────── */
static class SynchronizedAccessor implements Serializable {
private static SynchronizedAccessor instance;
private SynchronizedAccessor() { }
static synchronized SynchronizedAccessor get() {
if (instance == null) instance = new SynchronizedAccessor();
return instance;
}
}
/* ─────────── C ─────────── */
static class DoubleChecked implements Serializable {
private static DoubleChecked instance; // note what is missing
private DoubleChecked() { }
static DoubleChecked get() {
if (instance == null) {
synchronized (DoubleChecked.class) {
if (instance == null) instance = new DoubleChecked();
}
}
return instance;
}
}
/* ─────────── D ─────────── */
static class HolderClass implements Serializable {
private static class Inner { static final HolderClass INSTANCE = new HolderClass(); }
private HolderClass() { }
static HolderClass get() { return Inner.INSTANCE; }
private Object readResolve() { return Inner.INSTANCE; }
}
/* ─────────── E ─────────── */
enum EnumSingleton {
INSTANCE;
public final Map<String, String> cache = new HashMap<>();
}
public static void main(String[] args) throws Exception {
System.out.println("score these before running anything:");
System.out.println();
System.out.println(" lazy safe reflection serialization");
System.out.println(" A Eager ____ ____ __________ _____________");
System.out.println(" B Synchronized ____ ____ __________ _____________");
System.out.println(" C DoubleChecked ____ ____ __________ _____________");
System.out.println(" D HolderClass ____ ____ __________ _____________");
System.out.println(" E EnumSingleton ____ ____ __________ _____________");
// TODO 1: fill the table in. Three rows can be settled by reading.
// TODO 2: C has a defect that a test cannot expose — it may work for
// years on x86 and fail on ARM. Name it, name the one word that fixes
// it, and say why "I ran it and it was fine" is not evidence.
// TODO 3: write the reflection attack once and run it against all
// five. Four fall over. Print the results as a table.
// TODO 4: write the serialization round trip and run it against all
// five. Note which one needed a line of code to survive, and which one
// needed nothing.
// TODO 5: rank the five for a new codebase and defend the top two in
// two sentences.
// TODO 6: E scores perfectly on all four axes and is still wrong.
// Find the reason — it is on the line after INSTANCE — and say why
// this is the mistake the pattern discussion always hides.
// TODO 7: in a Spring application, which of these five would you
// actually write? Answer honestly.
}
}Run it locally:
cd exercises/java/creational-patterns/thread-safe-singleton/02-challenge
javac Starter.java -d /tmp/out && java -cp /tmp/out StarterHints
Hint 1
Score each on four axes before judging: lazy, thread-safe, reflection, serialization.
Hint 2
One of the five has a defect you cannot demonstrate by running it. Say which and why that makes it worse, not better.
Hint 3
Two of the five are equally correct on construction. What separates them is what happens after.
Hint 4
One is correct on all four axes and still wrong for a different reason. Look at what it exposes.
Done when
- All five are scored on all four axes, in a table
- The broken double-checked locking is identified and fixed with one word
- You ranked them and can defend the top two
- The one with correct construction and unsafe state is named