Java AIO并非真正的异步IO,而是Linux上用线程池+epoll模拟、Windows上基于IOCP的封装,实际仍是Reactor变种;其回调线程不可控、调试困难、性能无优势且生态支持弱,生产环境普遍采用NIO。

Java里AIO和NIO根本不是同一类东西
Java的AIO(AsynchronousChannelGroup + AsynchronousSocketChannel)和NIO(Selector + ByteBuffer + Channel)表面都叫“非阻塞IO”,但底层模型、API设计、线程行为完全不同。别被名字误导——AIO是操作系统级异步(Proactor),NIO是用户态事件轮询(Reactor),Java层压根没提供真正的Proactor实现,只是包装了OS的aio_read/aio_write。
用AsynchronousSocketChannel发请求时,回调不一定在IO线程执行
这是最常踩的坑:你以为CompletionHandler会像Netty的EventLoop一样固定在线程池里跑,其实它默认由ForkJoinPool.commonPool()或你传入的ExecutorService调度。一旦你在回调里做同步阻塞操作(比如调Thread.sleep(100)或等DB响应),整个线程池可能卡住。
- 务必显式传入专用线程池:
channel.write(buf, attachment, new MyHandler(), Executors.newFixedThreadPool(4)) -
AsynchronousServerSocketChannel.accept()的回调如果没指定Executor,默认用commonPool,而它默认并行度是CPU核心数,高并发下极易耗尽 - Windows上
AIO底层走IOCP,Linux上则通过epoll+ 线程池模拟(JDK 7–10),实际仍是Reactor变种,性能不比NIO高,还更难调试
Selector.select()和AsynchronousChannelGroup.awaitTermination()完全不是一回事
Selector是主动轮询就绪事件,线程可控;AsynchronousChannelGroup是后台托管线程池+系统回调,生命周期管理极容易出错。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 关闭
AsynchronousChannelGroup前必须确保所有AsynchronousChannel已关闭,否则awaitTermination()可能永远不返回 -
Selector可以反复复用,AsynchronousChannelGroup一旦shutdown()就不能再创建新Channel - 调试时
strace -e trace=io_submit,io_getevents在Linux上几乎看不到AIO调用(因为JDK用线程池模拟了),但strace -e trace=epoll_wait能清晰看到NIO的轮询行为
生产环境基本没人用Java AIO,不是因为写不出来,而是没必要
Netty、Undertow、Jetty全基于NIO(或直接封装epoll),不是技术力不够,是AIO在JVM生态里有硬伤:
立即学习“Java免费学习笔记(深入)”;
- Linux上AIO不支持TCP socket(仅支持文件),JDK被迫用线程池+
epoll模拟,失去异步意义 - 异常堆栈模糊:
IOException常抛在回调线程里,和业务逻辑线程割裂,排查时找不到原始调用上下文 - 连接数少时AIO反而更重——每次注册都要进内核,NIO一次
epoll_ctl注册后可复用多年 - Spring Boot 3.x默认Web容器是Tomcat 10+(NIO)或Jetty 12(NIO),连Servlet规范都没定义AIO语义
除非你在写JNI层对接特定硬件驱动,否则所谓“AIO高性能”在Java里就是个幻觉。真要榨干IO,不如把ByteBuffer池化、减少GC、用DirectBuffer,这些见效快得多。

















