Dispatchers.IO vs Dispatchers.Default for blocking calls

Read the explanation
What if

Four blocking calls on Dispatchers.Default, on a 2-CPU machine. Only two run at a time; the others wait for a thread.

Predict first

Four blocking calls launch on Dispatchers.Default on a 2-CPU machine. How many run at once?

0123456789launch ×4waits for its childrencall 1: Thread.sleepprintcall 3: Thread.sleepprintcall 2: Thread.sleepprintcall 4: Thread.sleepprint

Tap any block to see what that thread is doing.

  • Running
  • Waiting / parked
Step 1/4

main launches four blocking calls on Dispatchers.Default.

1fun main() = runBlocking<Unit> {2    repeat(4) { i ->3        launch(Dispatchers.Default) { // one thread per CPU: 2 here4            Thread.sleep(100) // a blocking call, like JDBC or a file read5            println("call ${i + 1} done on ${Thread.currentThread().name}")6        }7    }8}

Explanation

What just happened

Four blocking calls (Thread.sleep standing in for JDBC or a file read) were launched on Dispatchers.Default, on a machine with 2 CPUs. Default has one thread per CPU, so two calls ran while the other two waited for a free thread. Both threads spent all that time blocked, not computing.

Try the what-if switch

Rule of thumb

CPU work, like parsing or sorting: Dispatchers.Default. Blocking calls: withContext(Dispatchers.IO). Suspending calls, like delay(): any dispatcher, since they don’t hold a thread while they wait.

Watch out

Our test machine was set to 2 CPUs so the timeline is readable. On your machine Default has one thread per core, but the effect is the same once the blocking calls outnumber them.

Common questions

What's the difference between Dispatchers.IO and Dispatchers.Default?

Default has one thread per CPU core, for CPU work. IO adds threads, up to 64 by default, so blocking calls can wait side by side. They share one pool: both run on threads named DefaultDispatcher-worker-N.

Why are blocking calls bad on Dispatchers.Default?

A blocking call holds its thread while it waits. With one thread per core, a few blocking calls can take every Default thread, and all other Default coroutines wait.

How many threads does Dispatchers.IO have?

It runs at most 64 blocking calls at once by default. The 65th waits for one of them to finish.

Verified: this timeline matches a real program, checked in CI on Kotlin 2.4 with kotlinx.coroutines 1.11. How we verify. Unclear or wrong? Tell us