The CoroutineDispatcher that is designed for offloading blocking IO tasks to a shared pool of threads.

Additional threads in this pool are created and are shutdown on demand. The number of threads used by tasks in this dispatcher is limited by the value of "" (IO_PARALLELISM_PROPERTY_NAME) system property. It defaults to the limit of 64 threads or the number of cores (whichever is larger).

Elasticity for limited parallelism

Dispatchers.IO has a unique property of elasticity: its views obtained with CoroutineDispatcher.limitedParallelism are not restricted by the Dispatchers.IO parallelism. Conceptually, there is a dispatcher backed by an unlimited pool of threads, and both Dispatchers.IO and views of Dispatchers.IO are actually views of that dispatcher. In practice this means that, despite not abiding by Dispatchers.IO's parallelism restrictions, its views share threads and resources with it.

In the following example

// 100 threads for MySQL connection
val myMysqlDbDispatcher = Dispatchers.IO.limitedParallelism(100)
// 60 threads for MongoDB connection
val myMongoDbDispatcher = Dispatchers.IO.limitedParallelism(60)

the system may have up to 64 + 100 + 60 threads dedicated to blocking tasks during peak loads, but during its steady state there is only a small number of threads shared among Dispatchers.IO, myMysqlDbDispatcher and myMongoDbDispatcher.

Implementation note

This dispatcher and its views share threads with the Default dispatcher, so using withContext(Dispatchers.IO) { ... } when already running on the Default dispatcher typically does not lead to an actual switching to another thread. In such scenarios, the underlying implementation attempts to keep the execution on the same thread on a best-effort basis.

As a result of thread sharing, more than 64 (default parallelism) threads can be created (but not used) during operations over IO dispatcher.