0
# Dispatchers
1
2
Execution context control for coroutines including standard dispatchers and custom dispatcher creation. Dispatchers determine which thread or thread pool coroutines execute on.
3
4
## Capabilities
5
6
### CoroutineDispatcher Base Class
7
8
Abstract base class for all coroutine dispatchers.
9
10
```kotlin { .api }
11
/**
12
* Base class for coroutine dispatchers
13
*/
14
abstract class CoroutineDispatcher : ContinuationInterceptor {
15
/** Dispatch the coroutine to appropriate thread/context */
16
abstract fun dispatch(context: CoroutineContext, block: Runnable)
17
18
/** Check if dispatch is needed for the current context */
19
open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
20
21
/** Create a dispatcher with limited parallelism */
22
open fun limitedParallelism(parallelism: Int): CoroutineDispatcher
23
24
/** Operator overload for withContext-like behavior */
25
suspend operator fun <T> invoke(block: suspend CoroutineScope.() -> T): T
26
}
27
```
28
29
### Standard Dispatchers
30
31
Built-in dispatchers for common execution contexts.
32
33
```kotlin { .api }
34
/**
35
* Contains standard coroutine dispatchers
36
*/
37
object Dispatchers {
38
/** Optimized for CPU-intensive work */
39
val Default: CoroutineDispatcher
40
41
/** For UI operations (platform-specific) */
42
val Main: MainCoroutineDispatcher
43
44
/** For blocking I/O operations */
45
val IO: CoroutineDispatcher
46
47
/** Not confined to any specific thread */
48
val Unconfined: CoroutineDispatcher
49
}
50
```
51
52
**Usage Examples:**
53
54
```kotlin
55
import kotlinx.coroutines.*
56
57
// CPU-intensive work
58
val result = withContext(Dispatchers.Default) {
59
heavyComputation()
60
}
61
62
// I/O operations
63
val data = withContext(Dispatchers.IO) {
64
readFile("data.txt")
65
}
66
67
// UI updates (Android/Desktop)
68
withContext(Dispatchers.Main) {
69
updateUI(result)
70
}
71
72
// Unconfined execution
73
launch(Dispatchers.Unconfined) {
74
// Runs in caller thread until first suspension
75
println("Start: ${Thread.currentThread().name}")
76
delay(100)
77
println("After delay: ${Thread.currentThread().name}")
78
}
79
```
80
81
### Main Dispatcher
82
83
Specialized dispatcher for UI thread operations.
84
85
```kotlin { .api }
86
/**
87
* Dispatcher for UI operations
88
*/
89
abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
90
/** Immediate execution if already on main thread */
91
abstract val immediate: MainCoroutineDispatcher
92
93
/** Factory for creating main dispatcher */
94
companion object {
95
fun createDispatcher(): MainCoroutineDispatcher
96
}
97
}
98
```
99
100
**Usage Examples:**
101
102
```kotlin
103
// Regular main dispatcher
104
withContext(Dispatchers.Main) {
105
updateUI() // May dispatch even if already on main thread
106
}
107
108
// Immediate main dispatcher
109
withContext(Dispatchers.Main.immediate) {
110
updateUI() // No dispatch if already on main thread
111
}
112
```
113
114
### Custom Dispatchers
115
116
Creating custom dispatchers from executors and thread pools.
117
118
```kotlin { .api }
119
/**
120
* Executor-based dispatcher
121
*/
122
abstract class ExecutorCoroutineDispatcher : CoroutineDispatcher(), Closeable {
123
/** The underlying executor */
124
abstract val executor: Executor
125
126
/** Close the dispatcher and shutdown executor */
127
abstract override fun close()
128
}
129
130
/**
131
* Convert Executor to CoroutineDispatcher
132
*/
133
fun Executor.asCoroutineDispatcher(): ExecutorCoroutineDispatcher
134
135
/**
136
* Convert ExecutorService to CoroutineDispatcher
137
*/
138
fun ExecutorService.asCoroutineDispatcher(): ExecutorCoroutineDispatcher
139
140
/**
141
* Convert CoroutineDispatcher to Executor
142
*/
143
fun CoroutineDispatcher.asExecutor(): Executor
144
145
/**
146
* Create single-threaded dispatcher
147
*/
148
fun newSingleThreadContext(name: String): ExecutorCoroutineDispatcher
149
150
/**
151
* Create fixed thread pool dispatcher
152
*/
153
fun newFixedThreadPoolContext(nThreads: Int, name: String): ExecutorCoroutineDispatcher
154
```
155
156
**Usage Examples:**
157
158
```kotlin
159
import kotlinx.coroutines.*
160
import java.util.concurrent.*
161
162
// Custom thread pool
163
val customDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
164
165
// Use custom dispatcher
166
val result = withContext(customDispatcher) {
167
performWork()
168
}
169
170
// Always close custom dispatchers
171
customDispatcher.close()
172
173
// Single thread dispatcher
174
val singleThreadDispatcher = newSingleThreadContext("MyThread")
175
try {
176
launch(singleThreadDispatcher) {
177
// Runs on dedicated thread "MyThread"
178
serialProcessing()
179
}
180
} finally {
181
singleThreadDispatcher.close()
182
}
183
```
184
185
## Dispatcher Characteristics
186
187
### Dispatchers.Default
188
189
- **Purpose**: CPU-intensive computations
190
- **Thread Pool**: Shared pool sized to number of CPU cores
191
- **Use Cases**: Mathematical calculations, data processing, algorithms
192
- **Avoid**: Blocking I/O operations
193
194
```kotlin
195
// Good use of Default
196
val primes = withContext(Dispatchers.Default) {
197
findPrimesInRange(1..1000000)
198
}
199
200
// Bad use of Default - blocks threads
201
withContext(Dispatchers.Default) {
202
Thread.sleep(1000) // Don't do this!
203
}
204
```
205
206
### Dispatchers.IO
207
208
- **Purpose**: Blocking I/O operations
209
- **Thread Pool**: Larger pool that can grow as needed
210
- **Use Cases**: File operations, network calls, database access
211
- **Design**: Can handle thread blocking without starving other coroutines
212
213
```kotlin
214
// File I/O
215
val content = withContext(Dispatchers.IO) {
216
File("large-file.txt").readText()
217
}
218
219
// Network calls
220
val response = withContext(Dispatchers.IO) {
221
httpClient.get("https://api.example.com/data")
222
}
223
224
// Database operations
225
val users = withContext(Dispatchers.IO) {
226
database.query("SELECT * FROM users")
227
}
228
```
229
230
### Dispatchers.Main
231
232
- **Purpose**: UI thread operations
233
- **Thread**: Single UI thread (platform-specific)
234
- **Use Cases**: UI updates, view modifications
235
- **Platform**: Android, Swing, JavaFX, etc.
236
237
```kotlin
238
// UI updates
239
withContext(Dispatchers.Main) {
240
progressBar.progress = 100
241
statusLabel.text = "Complete"
242
}
243
```
244
245
### Dispatchers.Unconfined
246
247
- **Purpose**: Special dispatcher that doesn't confine execution
248
- **Behavior**: Starts in caller thread, resumes in whatever thread the suspended function uses
249
- **Use Cases**: Testing, specific performance scenarios
250
- **Caution**: Can lead to unpredictable thread switching
251
252
```kotlin
253
launch(Dispatchers.Unconfined) {
254
println("1: ${Thread.currentThread().name}")
255
delay(100) // May resume on different thread
256
println("2: ${Thread.currentThread().name}")
257
}
258
```
259
260
## Best Practices
261
262
### Choose Appropriate Dispatcher
263
264
```kotlin
265
// CPU work
266
val computed = withContext(Dispatchers.Default) {
267
expensiveCalculation()
268
}
269
270
// I/O work
271
val data = withContext(Dispatchers.IO) {
272
loadFromDatabase()
273
}
274
275
// UI updates
276
withContext(Dispatchers.Main) {
277
displayResult(computed, data)
278
}
279
```
280
281
### Avoid Unnecessary Context Switches
282
283
```kotlin
284
// Inefficient - multiple context switches
285
suspend fun processData() {
286
val data1 = withContext(Dispatchers.IO) { loadData1() }
287
val data2 = withContext(Dispatchers.IO) { loadData2() }
288
val data3 = withContext(Dispatchers.IO) { loadData3() }
289
}
290
291
// Better - single context switch
292
suspend fun processDataEfficient() {
293
withContext(Dispatchers.IO) {
294
val data1 = loadData1()
295
val data2 = loadData2()
296
val data3 = loadData3()
297
}
298
}
299
```
300
301
### Resource Management
302
303
```kotlin
304
// Always close custom dispatchers
305
val customDispatcher = newFixedThreadPoolContext(4, "Custom")
306
try {
307
// Use dispatcher
308
withContext(customDispatcher) {
309
performWork()
310
}
311
} finally {
312
customDispatcher.close()
313
}
314
315
// Or use 'use' extension
316
newSingleThreadContext("Worker").use { dispatcher ->
317
withContext(dispatcher) {
318
performWork()
319
}
320
} // Automatically closed
321
```
322
323
## JVM Integration
324
325
### CompletableFuture Integration
326
327
Coroutines can interoperate seamlessly with Java's CompletableFuture.
328
329
```kotlin { .api }
330
/**
331
* Start coroutine and return CompletableFuture
332
*/
333
fun <T> CoroutineScope.future(
334
context: CoroutineContext = EmptyCoroutineContext,
335
start: CoroutineStart = CoroutineStart.DEFAULT,
336
block: suspend CoroutineScope.() -> T
337
): CompletableFuture<T>
338
339
/**
340
* Convert Deferred to CompletableFuture
341
*/
342
fun <T> Deferred<T>.asCompletableFuture(): CompletableFuture<T>
343
344
/**
345
* Await CompletableFuture completion
346
*/
347
suspend fun <T> CompletableFuture<T>.await(): T
348
```
349
350
**Usage Examples:**
351
352
```kotlin
353
import kotlinx.coroutines.*
354
import java.util.concurrent.CompletableFuture
355
356
// Create CompletableFuture from coroutine
357
val future: CompletableFuture<String> = GlobalScope.future {
358
delay(100)
359
"Hello from coroutine"
360
}
361
362
// Use traditional CompletableFuture API
363
future.thenAccept { result ->
364
println(result)
365
}
366
367
// Await Java CompletableFuture in coroutine
368
suspend fun processJavaFuture() {
369
val javaFuture: CompletableFuture<String> = CompletableFuture.supplyAsync {
370
Thread.sleep(100)
371
"Hello from Java"
372
}
373
374
val result = javaFuture.await()
375
println(result)
376
}
377
378
// Convert Deferred to CompletableFuture
379
val deferred = async { computeValue() }
380
val future = deferred.asCompletableFuture()
381
```
382
383
### Thread Context Propagation
384
385
Support for propagating thread-local variables across coroutine boundaries.
386
387
```kotlin { .api }
388
/**
389
* Element for propagating thread-local data
390
*/
391
interface ThreadContextElement<S> : CoroutineContext.Element {
392
/** Update thread-local state and return previous state */
393
fun updateThreadContext(context: CoroutineContext): S
394
/** Restore previous thread-local state */
395
fun restoreThreadContext(context: CoroutineContext, oldState: S)
396
}
397
```