SwadhyAI Notes

All Low-Level Design notes

Concurrency · Launch page

Thread-safe object design

Thread-safe design means shared mutable state cannot be left half-updated when two threads run at once. Example: Gate A and Gate B both see spot 101 free. Both issue a ticket. Two cars, one spot. The bug is not merely “forgot synchronized.” The bug is a check-then-act on shared occupancy.

is the design that makes only one occupy() succeed. Say that story before you type a Java primitive.

Guarantee

Gate A ──┐
         ├── tries Spot #101
Gate B ──┘

Only one occupy() may succeed.

The real question

What is the shared mutable state, and what operation must be atomic?

Four honest stories

Thread safety is a story you can tell for every piece of shared mutable state. Mix them in one ParkingLot: Money is immutable, the allocator may be confined, occupy may be CAS, a cache node move may be locked.

Immutability

. Share them. Never lock them. If the fee changes, replace the Money; do not scratch a coin.

Confinement

owns the board. Gates only send requests. The list is not shared mutable state.

Optimistic CAS

: FREE → OCCUPIED. Loser takes the next spot. First Java answer for a single arrow.

Lock

, lock that object — not the servlet, not ParkingService. Claim, release, then print.

Parking Lot, told as four sentences

In Parking Lot the critical section is “choose a free spot and mark it occupied.” If those are two steps, you have a race. A strong first Java answer is tryOccupy on the spot with compareAndSet. The allocator loops free spots until one CAS wins. A lock on the lot is an honest second answer. Giving every spot to one allocator thread is confinement. Money and TicketId need none of this if they cannot change.

The interviewer is grading whether two gates can take 101, not whether you remember ReentrantLock’s fair flag. The is park(Vehicle). Two callers may enter. Design park so only one ticket is born for one spot.

Occupy is one step

After the story, this is the smallest Java that matches it. The rest of the lot can stay ordinary loops over tryOccupy.

java
class ParkingSpot {
  private final AtomicReference<SpotStatus> status =
      new AtomicReference<>(SpotStatus.FREE);

  boolean tryOccupy() {
    return status.compareAndSet(SpotStatus.FREE, SpotStatus.OCCUPIED);
  }
}

Ticket park(Vehicle v) {
  for (ParkingSpot s : spots) {
    if (s.fits(v) && s.tryOccupy()) return issueTicket(s, v);
  }
  throw new LotFullException();
}

How to explain this in an interview

  1. 1Name the shared mutable bits. Occupancy yes. Hourly rate no if Money is immutable.
  2. 2Name the operation that must be one step. Pick-and-occupy, or occupy-on-this-spot, or pay.
  3. 3If nothing mutates, share freely. If one thread can own it, confine. If one arrow, CAS. If several fields, lock that object.
  4. 4Say what the loser does. Next spot, LotFull, already paid. Then write the method.

What needs a story

  • Spot occupancy and ticket status arrows.
  • A HashMap of tickets by id if two gates insert.
  • Cache map plus recency list, as one critical section or two coordinated CAS-es.

What does not

  • Immutable Money, Duration, Email, VehicleType.
  • A local list inside a method that never escapes.
  • Read-only config loaded before threads start and never written again.

Publication is part of the story

An object is not thread-safe only in its methods. It is also how you publish it. A ParkingLot built on one thread and then used from two gates is fine if construction finished first. Storing it in a static non-volatile field and starting gates before the constructor returns is a torn lot. Safe publication: pass through a concurrent queue, assign to a volatile, or start threads after main finished building. Immutability plus safe publication is the cheapest pair in the catalog.

In an interview, after you name occupy, add one line: “Gates start after the lot is fully built; Money is immutable; occupy is CAS.” That is a complete thread-safety answer for parking, not a keyword dump.

Other LLD objects, same four stories

Rate limiter

Tokens remaining is shared mutable. CAS the count, or confine to one limiter thread. The clock Instant you pass in is a value.

In-process cache

Map plus recency list often want one lock for get-that-moves, or a concurrent map plus a careful eviction story. Values can be immutable snapshots.

Common mistakes

Synchronizing the printer, not the spot

The race is occupancy. A lock around issueTicket() still lets both gates pass isFree(). Protect the decision that assigns 101.

Trusting a single-thread test

park() looked fine in a main method. Two gates are two threads. If the object has shared mutable spots, the API must survive both callers.

One synchronized on the God object

Locking ParkingService serializes Saturday for a receipt. Tell a story per piece of state. Mix immutability, CAS, and a small lock.

A whiteboard you can reuse

Draw three columns: thing, mutates?, story. TicketId / no / share. Money / no / share. Spot.status / yes / CAS. Ticket.status / yes / CAS. List of spots / yes, if two allocators / lock or confine. Notification queue / yes / BlockingQueue. SmsClient / depends / confine to worker. That table is the design. Java fills the cells. If a cell says “synchronized on ParkingLot,” you have not finished the table; you have collapsed it.

When a new requirement arrives — EV charging, a third gate — add rows. Do not add a bigger lock by reflex. The four stories scale by being mixed, not by one monitor on the world.

Exercises

  1. List every mutable field on your ParkingLot sketch. For each, write one of: immutable, confined, CAS, lock.
  2. Movie hall: two counters, seat H12. Which of the four stories do you pick, and what does the loser show?
  3. You made Money mutable so you could add tax in place. What did you just add to the thread-safety budget?
  4. Lazy new ParkingLot() from two gates with a null check on a static field. Which of the four stories did you skip for the lot itself?

Fill the three-column table before you type synchronized. Thing, mutates?, story. Empty cells are the bugs. A God lock is a table with one row that lies.

Read next

Where “safe” still is not

A thread-safe spot and an unsafe ticket index still double-issue ids. A thread-safe map and a two-step occupy still double-park. Safety does not compose by default. Compose on purpose: each shared piece has a row in the table. Lazy init of a singleton ParkingLot with unsynchronized new is a race on the lot itself. Enum singleton or holder class, or just construct in main before gates start.

Takeaway

Thread safety is four stories per piece of shared mutable state. Name occupancy. Make occupy one step. Leave Money alone. The public method is the guarantee that two gates cannot both take 101.

Safety does not compose: a safe spot plus an unsafe index still double-issues. Publish the lot fully built. Fill the table. Mix the four stories. Do not collapse them into one synchronized on the world.