一、核心接口分工
SendChannel :作为通道的发送端,仅暴露数据写入能力。其核心方法为挂起函数 send(element: E),用于向通道提交数据。当缓冲区已满时,当前协程会自动挂起而非阻塞线程,同时支持 trySend 非阻塞尝试发送以及 close() 关闭通道等操作。
ReceiveChannel :作为通道的接收端,仅暴露数据读取能力。其核心方法为挂起函数 receive(): E,用于从通道取出数据。当通道为空时,当前协程会自动挂起等待新数据到达,同时支持 tryReceive 非阻塞尝试接收,亦可直接使用 for-in 循环遍历通道内所有元素直至通道关闭。
二、使用示例
kotlin
fun main() = runBlocking {
// 创建默认无缓冲通道,同时拥有发送端和接收端
val channel = Channel<Int>()
// 取出 SendChannel 侧,仅可用于发送数据
val sender: SendChannel<Int> = channel
// 取出 ReceiveChannel 侧,仅可用于接收数据
val receiver: ReceiveChannel<Int> = channel
// 生产者协程:通过 SendChannel 发送数据
launch {
repeat(5) {
sender.send(it)
println("Sent: $it")
}
sender.close() // 发送完成后关闭通道
}
// 消费者协程:通过 ReceiveChannel 接收数据
launch {
// for 循环自动遍历,通道关闭后自动结束循环
for (element in receiver) {
println("Received: $element")
}
println("通信结束")
}
delay(1000)
}
三、编译器生成的迭代逻辑
编译器实际生成的逻辑等价于:
scss
val iterator = receiver.iterator() // 获取迭代器
while (iterator.hasNext()) { // 判断是否有下一个元素
val element = iterator.next() // 获取下一个元素
println(element)
}
四、Channel 实现类
kotlin
public fun <E> Channel(
capacity: Int = RENDEZVOUS,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
onUndeliveredElement: ((E) -> Unit)? = null
): Channel<E> {
// 这里的 when 判断用于选择具体的实现类
return when (capacity) {
RENDEZVOUS -> RendezvousChannel(onUndeliveredElement) // 无缓冲,容量为 0
CONFLATED -> ConflatedChannel(onUndeliveredElement) // Conflated,容量为 -1
UNLIMITED -> LinkedListChannel(onUndeliveredElement) // 无限缓冲,使用链表
else -> ArrayChannel(capacity, onBufferOverflow, onUndeliveredElement) // 有界缓冲,使用数组
}
}
上述实现类均继承自共同的基类 AbstractChannel。
kotlin
// AbstractChannel.kt 简化源码逻辑
abstract class AbstractChannel<E>(
private val onUndeliveredElement: ((E) -> Unit)?
) : Channel<E>, SendChannel<E>, ReceiveChannel<E> {
// 1. 提供迭代器入口
public final override fun iterator(): ChannelIterator<E> = Itr(this)
// ... 其他 send/receive 逻辑
}
五、迭代器实现原理
kotlin
private class Itr<E>(@JvmField val channel: AbstractChannel<E>) : ChannelIterator<E> {
var result: Any? = POLL_FAILED // E | POLL_FAILED | Closed
override suspend fun hasNext(): Boolean {
// check for repeated hasNext
if (result !== POLL_FAILED) return hasNextResult(result)
// fast path -- try poll non-blocking
result = channel.pollInternal()
if (result !== POLL_FAILED) return hasNextResult(result)
// slow-path does suspend
return hasNextSuspend()
}
private fun hasNextResult(result: Any?): Boolean {
if (result is Closed<*>) {
if (result.closeCause != null) throw recoverStackTrace(result.receiveException)
return false
}
return true
}
private suspend fun hasNextSuspend(): Boolean = suspendCancellableCoroutineReusable sc@ { cont ->
val receive = ReceiveHasNext(this, cont)
while (true) {
if (channel.enqueueReceive(receive)) {
channel.removeReceiveOnCancel(cont, receive)
return@sc
}
// hm... something is not right. try to poll
val result = channel.pollInternal()
this.result = result
if (result is Closed<*>) {
if (result.closeCause == null)
cont.resume(false)
else
cont.resumeWithException(result.receiveException)
return@sc
}
if (result !== POLL_FAILED) {
@Suppress("UNCHECKED_CAST")
cont.resume(true, channel.onUndeliveredElement?.bindCancellationFun(result as E, cont.context))
return@sc
}
}
}
@Suppress("UNCHECKED_CAST")
override fun next(): E {
val result = this.result
if (result is Closed<*>) throw recoverStackTrace(result.receiveException)
if (result !== POLL_FAILED) {
this.result = POLL_FAILED
return result as E
}
throw IllegalStateException("'hasNext' should be called prior to 'next' invocation")
}
}
在 pollInternal 内部,this.result 被赋值为以下结果:
ini
val result = channel.pollInternal()
this.result = result
六、pollInternal 函数
kotlin
protected open fun pollInternal(): Any? {
while (true) {
val send = takeFirstSendOrPeekClosed() ?: return POLL_FAILED
val token = send.tryResumeSend(null)
if (token != null) {
assert { token === RESUME_TOKEN }
send.completeResumeSend()
return send.pollResult
}
// too late, already cancelled, but we removed it from the queue and need to notify on undelivered element
send.undeliveredElement()
}
}
protected fun takeFirstSendOrPeekClosed(): Send? =
queue.removeFirstIfIsInstanceOfOrPeekIf<Send> { it is Closed<*> }
七、内部队列机制
Channel 的设计灵感来源于 Java 中的 BlockingQueue,但其专为非阻塞挂起协程而设计。
生产者(SendChannel) :调用 send(element) 时,实际上是将数据元素封装成一个节点,放入这个内部队列的尾部。如果队列已满(对于有界 Channel),发送协程会被挂起,直到有空间可用。
消费者(ReceiveChannel) :调用 receive() 时,实际上是从这个内部队列的头部取出数据元素。如果队列为空,接收协程会被挂起,直到有新数据入队或通道关闭。