异步 NIO 的“回调”不是随便在哪里运行的函数。这篇用真实的环回连接量出它在哪个线程上运行、栈有多深、同一个通道上有哪些并发限制、write 与超时的完成语义,以及线程组为什么不终止。
accept 回调运行在线程 [aio-21];read 回调运行在线程 [aio-20](主线程是 main,固定线程池 2 个线程)
[默认]
sun.nio.ch.maxCompletionHandlersOnStack = 默认(未设置):每次读 1 字节、共 5000 次回调,运行在线程 [aio];回调里栈深度最小 11、最大 139,共 17 种深度
栈深度分布(前几种):[11×295, 19×295, 27×294, 35×294, 43×294, 51×294, 59×294, 67×294]
[-Dsun.nio.ch.maxCompletionHandlersOnStack=0]
sun.nio.ch.maxCompletionHandlersOnStack = 0:每次读 1 字节、共 5000 次回调,运行在线程 [aio];回调里栈深度最小 11、最大 11,共 1 种深度
栈深度分布(前几种):[11×5000]
[-Dsun.nio.ch.maxCompletionHandlersOnStack=64]
sun.nio.ch.maxCompletionHandlersOnStack = 64:每次读 1 字节、共 5000 次回调,运行在线程 [aio];回调里栈深度最小 11、最大 523,共 65 种深度
栈深度分布(前几种):[11×77, 19×77, 27×77, 35×77, 43×77, 51×77, 59×77, 67×77]已有一个 read 未完成时再调 read:ReadPendingException 第一个 read 之后完成:3 字节 对端不读,连续发起 1 MiB 的 write:第 7 个 write 在 50 ms 后仍未完成(此前每个都立即完成了) 已有一个 write 未完成时再调 write:WritePendingException 已连接的通道再 connect:AlreadyConnectedException 未连接就 read:NotYetConnectedException 用只读 ByteBuffer 做 read 的目标:IllegalArgumentException
对端一直不读时,write(16 MiB 的 ByteBuffer) 第一次完成写了 327212 字节(= 319 KiB),缓冲区还剩 16450004 字节,耗时 2 ms 对端开始读之后,要连续调用 write 10 次才写完 16 MiB(共 16777216 字节)
read(300 ms 超时) 的 failed 回调:InterruptedByTimeoutException,用时 313 ms,回调线程 Thread-12 超时之后再 read:IllegalStateException 超时之后通道 isOpen = true
accept 回调在线程 aio-1 里抛出 RuntimeException 默认 UncaughtExceptionHandler 收到(线程 aio-1):java.lang.RuntimeException: 回调里的异常 回调抛异常之后:ThreadFactory 一共创建了 2 个线程;下一个 accept 的 Future 在 2 秒内完成了 group.shutdown() 之后 isShutdown = true,isTerminated = false(还有打开的通道) awaitTermination(500 ms) = false 关闭了客户端通道 cl、cl2 和监听通道 srv 之后 awaitTermination(1 s) = false(accept 得到的两个服务端通道还开着) 再关闭第二个 accept 得到的通道之后 awaitTermination(1 s) = false 最后关闭第一个 accept 得到的通道(就是抛异常的回调里拿到的那个)之后 awaitTermination(2 s) = true,isTerminated = true,耗时 2537 ms