Channel Basics: send, receive, and close
Create channels, send and receive values, and close them properly.
Channel Basics: send, receive, and close is a free Kotlin Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is a Channel?
A Channel is a coroutine primitive for communicating between coroutines — like a blocking queue but suspending. Think of it as a pipe: one side sends, the other receives.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val channel = Channel<Int>()
launch {
for (i in 1..5) channel.send(i)
channel.close()
}
for (x in channel) println(x) // 1 2 3 4 5
}send and receive
send(value) suspends until the receiver is ready. receive() suspends until a value is available.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val ch = Channel<String>()
launch {
ch.send("ping")
println("sent")
}
val msg = ch.receive()
println("received: $msg")
}Closing a Channel
channel.close() signals that no more values will be sent. Receivers get remaining buffered values, then the for-loop ends.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val ch = Channel<Int>(3)
launch {
repeat(3) { ch.send(it) }
ch.close() // signal done
}
for (v in ch) println(v) // 0 1 2, then loop ends
println("Channel consumed")
}isClosedForSend / isClosedForReceive
Check channel state with isClosedForSend (no more sends allowed) and isClosedForReceive (all values consumed).
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val ch = Channel<Int>()
launch {
ch.send(1)
ch.close()
}
println(ch.receive())
println("isClosedForReceive: ${ch.isClosedForReceive}")
}produce Builder
produce { } is a coroutine builder that creates a producer coroutine and returns a ReceiveChannel. The channel closes automatically when the block ends.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.squares(n: Int) = produce<Int> {
for (i in 1..n) send(i * i)
}
fun main() = runBlocking {
val sq = squares(5)
for (v in sq) println(v) // 1 4 9 16 25
}consumeEach Extension
consumeEach { } iterates a channel and cancels it when the block throws, preventing leaks.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val ch = produce<String> {
send("a"); send("b"); send("c")
}
ch.consumeEach { println(it) }
}tryReceive and trySend
Non-suspending alternatives: trySend / tryReceive return a ChannelResult immediately without suspending.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val ch = Channel<Int>(1)
val sendResult = ch.trySend(42)
println("Sent: ${sendResult.isSuccess}")
val recvResult = ch.tryReceive()
println("Got: ${recvResult.getOrNull()}")
}Fan-Out: Multiple Consumers
Multiple coroutines can receive from the same channel, distributing work — a fan-out pattern.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val tasks = produce { repeat(6) { send(it) } }
repeat(3) { worker ->
launch {
for (task in tasks) {
println("Worker $worker: task $task")
}
}
}
}Fan-In: Multiple Producers
Multiple coroutines can send to the same channel, merging their outputs — a fan-in pattern.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val results = Channel<String>()
repeat(3) { i ->
launch { results.send("result-$i") }
}
launch {
repeat(3) { println(results.receive()) }
results.close()
}
}Pipeline Pattern
Chain channels into a pipeline where each stage reads from one channel and writes to another — great for data processing workflows.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.numbers() = produce { for (i in 1..5) send(i) }
fun CoroutineScope.doubled(input: ReceiveChannel<Int>) = produce { for (v in input) send(v * 2) }
fun main() = runBlocking {
val nums = numbers()
val doubled = doubled(nums)
for (v in doubled) println(v) // 2 4 6 8 10
coroutineContext.cancelChildren()
}Channel vs Flow
Channels are hot and stateful — they exist independently of consumption. Flows are cold and declarative — they restart on each collection. Prefer Flow for data streams; use Channel for producer-consumer communication.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.*
// Flow (cold): each collect() re-runs the block
// Channel (hot): shared; multiple receivers share the same stream
fun main() = runBlocking { println("Flow = cold; Channel = hot") }Quick Check
What happens when you call channel.close()?
Recap
send and receive suspend until the other side is ready. close() signals completion. Use produce for self-closing producers, consumeEach for safe iteration, and channels for fan-out/fan-in patterns.
Frequently asked questions
Is the “Channel Basics: send, receive, and close” lesson free?
Yes — the full text of “Channel Basics: send, receive, and close” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.
What will I learn in “Channel Basics: send, receive, and close”?
Create channels, send and receive values, and close them properly. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Kotlin Academy?
No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Channel Basics: send, receive, and close” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Kotlin Academy lesson?
Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Channel Basics: send, receive, and close
- Channel Types: Rendezvous, Buffered, Conflated, Unlimited
- Mutex and Semaphore for Shared State
- Actors and Structured State Management