Threads, Executors & Thread Pools Quiz

KOTLIN › JVM Concurrency

A thread is parked inside a blocking socket read. What state does a thread dump report?

Answer: RUNNABLE, since BLOCKED means contention on a monitor lock

BLOCKED is reserved for monitor-lock contention. A thread in a blocking system call is reported RUNNABLE even though it is doing nothing, which makes IO-heavy dumps look busier than they are.

Why is Future.get() the limitation that drove the JVM towards other models?

Answer: It blocks the caller, so composing async work wastes a pooled thread

No completion callback means waiting means blocking, defeating the purpose of a pool. CompletableFuture fixed composition, and coroutines gave it sequential syntax.

What makes Executors.newCachedThreadPool() risky under a burst?

Answer: Its queue holds nothing, so every task with no free thread creates a new one

A SynchronousQueue has zero capacity, so thread creation is unbounded and Android reports OutOfMemoryError from pthread_create. The fixed pool fails the other way, via its unbounded queue.

A ThreadPoolExecutor has core 2, max 50, unbounded queue. How many threads run?

Answer: Exactly 2, since the pool queues before growing and the queue never fills

Growth past core size is triggered only by a full queue, which an unbounded queue never reports. The maximum is dead configuration and the queue grows without bound instead.

Which rejection policy acts as a natural brake on a fast producer?

Answer: CallerRunsPolicy, which runs the task on the submitting thread

Running the task on the submitter blocks the producer for its duration, slowing submission to match capacity. The others either fail loudly or lose work silently.

A task waits 90ms on IO and computes for 10ms on a 4-core device. What pool size does the formula give?

Answer: About 40, from cores times one plus the wait-to-compute ratio

Four cores times (1 + 9) is forty. A thread parked on a socket uses no CPU, so you want enough that some are always runnable when cores free up.

Your lambda passed to thenApply returns a CompletableFuture. What is the result type?

Answer: CompletableFuture<CompletableFuture<T>>, meaning you wanted thenCompose

thenApply is map and thenCompose is flatMap. The nested type is the standard symptom of picking the mapping variant when flattening was needed.

A task submitted with submit() throws and nobody calls get(). What happens?

Answer: The exception is stored in the Future and lost silently

submit captures the failure into the Future for retrieval at get(). Silent task failure in production is nearly always this. execute has nowhere to store it, so it surfaces via the uncaught handler.

What memory guarantee does submitting to an executor provide?

Answer: Actions before submit() happen-before the task executes

It is the same edge as Thread.start(), so handing data into a task is safe. Sharing mutable state between concurrently running tasks still needs synchronisation.

Which API genuinely influences the Linux scheduler on Android?

Answer: Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND)

android.os.Process maps to Linux scheduling and cgroups, so background threads get a small CPU share and cannot starve rendering. The java.lang priority is largely advisory here.

Why was AsyncTask deprecated?

Answer: It leaked the Activity, had no lifecycle awareness, and silently serialised parallel work

Its inner-class reference held the Activity, onPostExecute ran against destroyed views, and the default executor became serial in API 11 so parallel work quietly queued.

Why does blocking inside a coroutine on Dispatchers.IO still matter?

Answer: The dispatcher is a real thread pool, so blocking occupies one of its finite threads

Coroutines multiplex onto ordinary pools rather than replacing them. Nothing forbids blocking and nothing moves it, which is why the 64-thread ceiling is a genuine limit.

What is the difference between shutdown() and shutdownNow()?

Answer: shutdown() lets queued tasks run; shutdownNow() interrupts running tasks and returns the queued ones

Neither blocks, which is what awaitTermination is for. Failing to shut down leaves non-daemon threads alive, which can keep a JVM process running after its work is done.

Back to Threads, Executors & Thread Pools