AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

17 Concurrency / Checkpoint

Checkpoint — Concurrency

30 min · +25 XP · 3 exercises · 15 questions

Exercises0/3
Quiz · 70% to clear15 q

This checkpoint covers threads and the interrupt protocol, races and synchronized, the Java Memory Model with volatile and safe publication, executors and Futures, ReentrantLock, atomics and the concurrent collections, CompletableFuture, and the coordination tools — latch, barrier, semaphore and blocking queue.

How it works. Fifteen questions and three programs; 70% on the questions and every program accepted clears the module.

Before you start, make sure you can answer:

  • start() versus run(), and how a thread is stopped politely.
  • Why count++ is a race, and the two guarantees synchronized gives.
  • What volatile does and does not do, and why double-checked locking needs it.
  • Which happens-before edge makes results written by a worker visible after join().
  • Fixed versus cached pool, what Future.get() throws, and the shutdown sequence.
  • Why ConcurrentHashMap.merge removes the check-then-act race a synchronised HashMap has.
  • thenApply versus thenCompose, and which thread runs a thenX stage.
  • CountDownLatch versus CyclicBarrier, and how a poison pill ends a consumer.

The programs are a parallel row-sum on a fixed pool with results in order, a bank that transfers money between accounts under per-account locks acquired in a global order, and a phased simulation on a CyclicBarrier whose barrier action reports each round.

Common questions

Which happens-before edge makes a worker's results visible after join()?

Every action in a thread happens-before another thread's join() on it returns. So once join() returns, everything the worker wrote — an array slot, a field — is visible to the joining thread without any further synchronisation.

Why does ConcurrentHashMap merge remove the check-then-act race?

merge reads the current value, applies the function and writes the result as one atomic operation per key. With a synchronised HashMap, get and put are two separately locked calls, and another thread can update the key between them.

How do you transfer money between two accounts without deadlock?

Lock both accounts in one global order — for example the smaller account id first — whichever direction the money moves. Two transfers in opposite directions then request the locks in the same order and cannot each hold one while waiting for the other.

Java Concurrency Quiz: Threads, Locks and Executors Practice

Test Java concurrency with 15 questions and three programs on threads, synchronized, volatile, executors, atomics, ConcurrentHashMap and CompletableFuture.

  • Course: Java study plan
  • Module: Concurrency
  • Kind: Checkpoint — cleared at 70%
  • Reading time: 30 min
  • Runtime: OpenJDK 18

Checkpoint — Concurrency is the checkpoint that closes the Concurrency module: a graded quiz and whole-program exercises, passed at 70%.

Instructions

This checkpoint covers threads and the interrupt protocol, races and synchronized, the Java Memory Model with volatile and safe publication, executors and Futures, ReentrantLock, atomics and the concurrent collections, CompletableFuture, and the coordination tools — latch, barrier, semaphore and blocking queue.

How it works. Fifteen questions and three programs; 70% on the questions and every program accepted clears the module.

Before you start, make sure you can answer:

  • start() versus run(), and how a thread is stopped politely.
  • Why count++ is a race, and the two guarantees synchronized gives.
  • What volatile does and does not do, and why double-checked locking needs it.
  • Which happens-before edge makes results written by a worker visible after join().
  • Fixed versus cached pool, what Future.get() throws, and the shutdown sequence.
  • Why ConcurrentHashMap.merge removes the check-then-act race a synchronised HashMap has.
  • thenApply versus thenCompose, and which thread runs a thenX stage.
  • CountDownLatch versus CyclicBarrier, and how a poison pill ends a consumer.

The programs are a parallel row-sum on a fixed pool with results in order, a bank that transfers money between accounts under per-account locks acquired in a global order, and a phased simulation on a CyclicBarrier whose barrier action reports each round.

Common questions

Which happens-before edge makes a worker's results visible after join()?

Every action in a thread happens-before another thread's join() on it returns. So once join() returns, everything the worker wrote — an array slot, a field — is visible to the joining thread without any further synchronisation.

Why does ConcurrentHashMap merge remove the check-then-act race?

merge reads the current value, applies the function and writes the result as one atomic operation per key. With a synchronised HashMap, get and put are two separately locked calls, and another thread can update the key between them.

How do you transfer money between two accounts without deadlock?

Lock both accounts in one global order — for example the smaller account id first — whichever direction the money moves. Two transfers in opposite directions then request the locks in the same order and cannot each hold one while waiting for the other.

Exercises

Row sums on a pool

Read r, c and a pool size p, then an r × c matrix of integers. On a fixed pool of p threads, submit one Callable<Long> per row with invokeAll, then print row <i>: <sum> for every row in order and total=<sum of all>. Shut the pool down, await termination, and print terminated=true.

Example: 2 3 2 then 1 2 3 / 4 5 6 →

row 0: 6
row 1: 15
total=21
terminated=true

Transfers with ordered locks

Read an integer n and n opening balances, then t and t transfers from to amount. Give each account its own ReentrantLock. Run every transfer on its own thread; a transfer locks the two accounts in ascending index order (lower index first — the rule that makes deadlock impossible), moves the money (balances may go negative), and unlocks both in finally blocks. Join all, then print balances=<Arrays.toString>, total=<sum> and preserved=<total equals opening total>.

Example: 3 then 100 50 0, 3 then 0 1 30, 1 2 60, 2 0 10 →

balances=[80, 20, 50]
total=150
preserved=true

Rounds at the barrier

Read k (parties), r (rounds) and k integer values. Create a CyclicBarrier(k, action) whose action prints round <j>: total=<running total> — it runs once per round, on the last thread to arrive. Each of the k threads, for rounds j = 1 … r, adds value × j to a shared AtomicLong and then await()s the barrier. Join all threads and print final=<total>. After round j the total is Σvalues × (1 + 2 + … + j) — the barrier makes that deterministic.

Example: 2 3 then 1 2 →

round 1: total=3
round 2: total=9
round 3: total=18
final=18

In this module: Concurrency

← CompletableFuture and the coordination tools · Bytes and characters — streams, readers and encodings →