Java基础-AIO
AIO
More New IO,或称 NIO.2,随 JDK 1.7 发布,包括了引入异步 IO 接口和 Paths 等文件访问接口。
异步这个词,我想对于绝大多数开发者来说都很熟悉,很多场景下我们都会使用异步。
通常,我们会有一个线程池用于执行异步任务,提交任务的线程将任务提交到线程池就可以立马返回,不必等到任务真正完成。如果想要知道任务的执行结果,通常是通过传递一个回调函数的方式,任务结束后去调用这个函数。
同样的原理,Java 中的异步 IO 也是一样的,都是由一个线程池来负责执行任务,然后使用回调或自己去查询结果。
大部分开发者都知道为什么要这么设计了,这里再啰嗦一下。异步 IO 主要是为了控制线程数量,减少过多的线程带来的内存消耗和 CPU 在线程调度上的开销。
在 Unix/Linux 等系统中,JDK 使用了并发包中的线程池来管理任务,具体可以查看 AsynchronousChannelGroup 的源码。
在 Windows 操作系统中,提供了一个叫做 I/O Completion Ports 的方案,通常简称为 IOCP,操作系统负责管理线程池,其性能非常优异,所以在 Windows 中 JDK 直接采用了 IOCP 的支持,使用系统支持,把更多的操作信息暴露给操作系统,也使得操作系统能够对我们的 IO 进行一定程度的优化。
- AsynchronousSocketChannel
- AsynchronousServerSocketChannel
- AsynchronousFileChannel
这三个类对应的NIO中SocketChannel, ServerSocketChannel,FileChannel
Java 异步 IO 提供了两种使用方式,分别是返回 Future 实例和使用回调函数。
- 返回 Future 实例
没错,就是java.util.concurrent.Future 实例 - 提供 CompletionHandler 回调函数
public interface CompletionHandler<V,A> {
void completed(V result, A attachment);
void failed(Throwable exc, A attachment);
}
AsynchronousFileChannel
- 实例化:
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get("/Users/hongjie/test.txt"));
- 读入到 Buffer 中:
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> result = channel.read(buffer, 0);
- 除了使用返回 Future 实例的方式,也可以采用回调函数:
public abstract <A> void read(ByteBuffer dst,long position, A attachment,CompletionHandler<Integer,? super A> handler);
AsynchronousServerSocketChannel
非阻塞 IO 的 ServerSocketChannel
AsynchronousSocketChannel
Asynchronous Channel Groups
为了知识的完整性,有必要对 group 进行介绍,其实也就是介绍 AsynchronousChannelGroup 这个类。之前我们说过,异步 IO 一定存在一个线程池,这个线程池负责接收任务、处理 IO 事件、回调等。这个线程池就在 group 内部,group 一旦关闭,那么相应的线程池就会关闭。
- AsynchronousServerSocketChannels 和 AsynchronousSocketChannels 是属于 group 的,当我们调用 AsynchronousServerSocketChannel 或 AsynchronousSocketChannel 的 open() 方法的时候,相应的 channel 就属于默认的 group,这个 group 由 JVM 自动构造并管理。
- 如果我们想要配置这个默认的 group,可以在 JVM 启动参数中指定以下系统变量:
java.nio.channels.DefaultThreadPool.threadFactory
此系统变量用于设置 ThreadFactory,它应该是 java.util.concurrent.ThreadFactory 实现类的全限定类名。一旦我们指定了这个 ThreadFactory 以后,group 中的线程就会使用该类产生。
java.nio.channels.DefaultThreadPool.initialSize
此系统变量也很好理解,用于设置线程池的初始大小。
-
可能你会想要使用自己定义的 group,这样可以对其中的线程进行更多的控制,使用以下几个方法即可:
1 AsynchronousChannelGroup.withCachedThreadPool(ExecutorService executor, int initialSize)
2 AsynchronousChannelGroup.withFixedThreadPool(int nThreads, ThreadFactory threadFactory)
3 AsynchronousChannelGroup.withThreadPool(ExecutorService executor) -
至于 group 的使用:
AsynchronousChannelGroup group = AsynchronousChannelGroup
.withFixedThreadPool(10, Executors.defaultThreadFactory());
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group);
AsynchronousSocketChannel client = AsynchronousSocketChannel.open(group);
小结
- •BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
- •NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
- •AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。