What is a Channel<T> in Kotlin coroutines, and how do producer and consumer coroutines communicate through it?
answer
- send/receive are suspend
- hot, not cold like Flow
- RENDEZVOUS = no buffer, direct handover
- for (x in channel) iterates receiver
- close() ends the loop
basics
~10 sA Channel is a pipe between coroutines. One coroutine puts values in with send, another takes them out with receive. Both suspend (wait politely) until the other side is ready.
solid answer
~40 sChannel<T> is a coroutine-safe queue used to pass a stream of values between coroutines. A producer calls suspend fun send(value), a consumer calls suspend fun receive(). Both are suspending functions: with the default RENDEZVOUS capacity, send suspends until a receiver is ready and receive suspends until a value is available, so the channel hands values over directly with no buffering. Unlike Flow (cold), a Channel is hot: values flow as soon as both ends meet, regardless of whether anyone is collecting yet. You create one with Channel<Int>() and typically iterate the consumer side with for (x in channel) or channel.consumeEach { }. When the producer is done it calls close(), which lets the consumer loop end cleanly.
code
kotlin · 6 linesval channel = Channel<Int>()
launch {
for (x in 1..3) channel.send(x)
channel.close()
}
for (y in channel) println(y) // 1 2 3go deeper
Knows send/receive exist, that they suspend, and that close ends the loop.
Explains hot vs cold vs Flow and the rendezvous handover precisely.
Discusses when a Channel is the right tool vs a Flow and the resource/ownership implications.
Frames Channel as the low-level communication primitive underlying produce/actor and shared-flow design tradeoffs.
## What a Channel is A `Channel<T>` is a concurrency primitive in `kotlinx.coroutines` for passing a *stream* of values of type `T` between coroutines. Think of it as a coroutine-aware `BlockingQueue`: instead of blocking a thread, its operations **suspend** the calling coroutine, freeing the thread to do other work. ## The two core operations - `suspend fun send(element: T)` — a **producer** offers a value into the channel. - `suspend fun receive(): T` — a **consumer** takes the next value out. Both are `suspend` functions, so they can only be called from a coroutine or another suspend function. "Suspend" means the coroutine pauses without blocking the underlying thread and resumes later when the operation can proceed. ## Hot vs cold A Channel is a **hot** stream: it exists and conducts values independently of consumers. This contrasts with `Flow`, which is **cold** (the producing code reruns for each collector). A Channel models *communication between coroutines*; a Flow models a *cold declarative pipeline*. ## Rendezvous by default With no capacity argument, `Channel<T>()` uses `RENDEZVOUS`: there is no buffer. `send` suspends until some coroutine calls `receive`, and vice versa. The value is handed over directly — a meeting point ("rendezvous"). ## Iterating the consumer side The channel implements `ReceiveChannel<T>`, which is iterable in a coroutine: ```kotlin import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { val channel = Channel<Int>() launch { // producer coroutine for (x in 1..3) channel.send(x * x) channel.close() // signal: no more values } for (y in channel) { // suspends until each value / close println(y) // 1, 4, 9 } } ``` The `for (y in channel)` loop calls `receive` under the hood and ends when the channel is **closed and drained**. ## Closing `close()` marks the channel as done. Pending buffered values are still delivered; afterwards the consumer loop terminates. Calling `send` on a closed channel throws `ClosedSendChannelException`. ## Why use one Channels are the building block for producer/consumer pipelines, fan-out/fan-in work distribution, and the higher-level `produce { }` and `actor { }` builders.
- How is a Channel different from a Flow?Channel is hot and is a communication primitive between coroutines; Flow is cold and reruns its producer per collector. Channels have explicit send/receive; Flow has emit/collect.
- What happens to receive when the channel is closed and empty?receive() throws ClosedReceiveChannelException; the for-loop and consumeEach handle this for you and just exit.
Like passing a baton in a relay: the runner (sender) waits with the baton until the next runner (receiver) is there to take it.
saying these in an interview costs you the question
- Saying a Channel is cold or reruns per consumer
- Claiming send/receive block the thread instead of suspending
- Thinking you can call send/receive from a normal (non-suspend) function
- Forgetting to close() and wondering why the consumer loop hangs forever