转载自 java aio 编程

Java NIO (JSR 51)定义了Java new I/O API,提案2000年提出,2002年正式发布。 JDK 1.4起包含了相应的API实现。
JAVA NIO2 (JSR 203)定义了更多的 New I/O APIs, 提案2003提出,直到2011年才发布, 最终在JDK 7中才实现。
JSR 203除了提供更多的文件系统操作API(包括可插拔的自定义的文件系统), 还提供了对socket和文件的异步 I/O操作。 同时实现了JSR-51提案中的socket channel全部功能,包括对绑定, option配置的支持以及多播multicast的实现。

当前很多的项目还停留在JAVA NIO的实现上, 对JAVA AIO(asynchronous I/O)着墨不多。 本文整理了一些关于JAVA AIO的介绍,以及netty对AIO的支持。
以下内容只针对socket的I/O操作, 不涉及对文件的处理。

JDK AIO API

首先介绍以下I/O模型。
Unix定义了五种I/O模型, 下图是五种I/O模型的比较。

  • 阻塞I/O
  • 非阻塞I/O
  • I/O复用(select、poll、linux 2.6种改进的epoll)
  • 信号驱动IO(SIGIO)
  • 异步I/O(POSIX的aio_系列函数)


UNP Ch6.2 I/O models

POSIX把I/O操作划分成两类:

  • 同步I/O: 同步I/O操作导致请求进程阻塞,直至操作完成
  • 异步I/O: 异步I/O操作不导致请求阻塞
    所以Unix的前四种I/O模型都是同步I/O, 只有最后一种才是异步I/O。

传统的Java BIO (blocking I/O)是Unix I/O模型中的第一种。
Java NIO中如果不使用select模式,而只把channel配置成nonblocking则是第二种模型。
Java NIO select实现的是一种多路复用I/O。 底层使用epoll或者相应的poll系统调用, 参看我以前整理的一篇文章: java 和netty epoll实现 
第四种模型JDK应该是没有实现。
Java NIO2增加了对第五种模型的支持,也就是AIO。

OpenJDK在不同平台上的AIO实现

在不同的操作系统上,AIO由不同的技术实现。
通用实现可以查看这里。
Windows上是使用完成接口(IOCP)实现,可以参看WindowsAsynchronousServerSocketChannelImpl,
其它平台上使用aio调用UnixAsynchronousServerSocketChannelImpl, UnixAsynchronousSocketChannelImpl, SolarisAsynchronousChannelProvider

常用类

  • AsynchronousSocketChannel

    • Asynchronous connect
    • Asynchronous read/write
    • Asynchronous scatter/gather (multiple buffers)
    • Read/write operations support timeout
    • failed method invoked with timeout exception
    • Implements NetworkChannel for binding, setting socket options, etc

AsynchronousServerSocketChannel
还实现了Asynchronous accept

AsynchronousDatagramChannel

  • Asynchronous read/write (connected)
  • Asynchronous receive/send (unconnected)
  • Implements NetworkChannel for binding, setting socket options, etc.
  • Implements MulticastChannel
  • CompletionHandler

Java AIO 例子

异步channel API提供了两种方式监控/控制异步操作(connect,accept, read,write等)。第一种方式是返回java.util.concurrent.Future对象, 检查Future的状态可以得到操作是否完成还是失败,还是进行中, future.get阻塞当前进程。
第二种方式为操作提供一个回调参数java.nio.channels.CompletionHandler,这个回调类包含completed,failed两个方法。
channel的每个I/O操作都为这两种方式提供了相应的方法, 你可以根据自己的需要选择合适的方式编程。

下面以一个最简单的Time服务的例子演示如何使用异步I/O。 客户端连接到服务器后服务器就发送一个当前的时间字符串给客户端。 客户端毋须发送请求。 逻辑很简单。

Server实现

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class Server {private static Charset charset = Charset.forName("US-ASCII");private static CharsetEncoder encoder = charset.newEncoder();public static void main(String[] args) throws Exception {AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(4));AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress("0.0.0.0", 8013));server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel result, Void attachment) {server.accept(null, this); // 接受下一个连接try {String now = new Date().toString();ByteBuffer buffer = encoder.encode(CharBuffer.wrap(now + "\r\n"));//result.write(buffer, null, new CompletionHandler<Integer,Void>(){...}); //callback orFuture<Integer> f = result.write(buffer);f.get();System.out.println("sent to client: " + now);result.close();} catch (IOException | InterruptedException | ExecutionException e) {e.printStackTrace();}}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}});group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);}
}

Client实现这个例子使用了两种方式。 accept使用了回调的方式, 而发送数据使用了future的方式。

public class Client {public static void main(String[] args) throws Exception {AsynchronousSocketChannel client = AsynchronousSocketChannel.open();Future<Void> future = client.connect(new InetSocketAddress("127.0.0.1", 8013));future.get();ByteBuffer buffer = ByteBuffer.allocate(100);client.read(buffer, null, new CompletionHandler<Integer, Void>() {@Overridepublic void completed(Integer result, Void attachment) {System.out.println("client received: " + new String(buffer.array()));}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();try {client.close();} catch (IOException e) {e.printStackTrace();}}});Thread.sleep(10000);}
}

Netty AIO客户端也使用了两种方式, connect使用了future方式,而接收数据使用了回调的方式。

Netty也支持AIO并提供了相应的类: AioEventLoopGroup,AioCompletionHandlerAioServerSocketChannelAioSocketChannel, AioSocketChannelConfig
其它使用方法和NIO类似。

io.netty.buffer和java.nio.ByteBuffer的区别

官方文档Using as a generic library描述了两者的区别,主要还是友好性,扩展和性能的考虑。
http://zizihaier.iteye.com/blog/1767409也提到:

ByteBuffer主要有两个继承的类分别是:HeapByteBuffer和MappedByteBuffer。他们的不同之处在于HeapByteBuffer会在JVM的堆上分配内存资源,而MappedByteBuffer的资源则会由JVM之外的操作系统内核来分配。DirectByteBuffer继承了MappedByteBuffer,采用了直接内存映射的方式,将文件直接映射到虚拟内存,同时减少在内核缓冲区和用户缓冲区之间的调用,尤其在处理大文件方面有很大的性能优势。但是在使用内存映射的时候会造成文件句柄一直被占用而无法删除的情况,网上也有很多介绍。
Netty中使用ChannelBuffer来处理读写,之所以废弃ByteBuffer,官方说法是ChannelBuffer简单易用并且有性能方面的优势。在ChannelBuffer中使用ByteBuffer或者byte[]来存储数据。同样的,ChannelBuffer也提供了几个标记来控制读写并以此取代ByteBuffer的position和limit,分别是:
0 <= readerIndex <= writerIndex <= capacity,同时也有类似于mark的markedReaderIndex和markedWriterIndex。当写入buffer时,writerIndex增加,从buffer中读取数据时readerIndex增加,而不能超过writerIndex。有了这两个变量后,就不用每次写入buffer后调用flip()方法,方便了很多。

Netty的零拷贝(zero copy)

Netty的“零拷贝”主要体现在如下三个方面:

1) Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
2) Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
3) Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

参考

  1. Asynchronous I/O Tricks and Tips
  2. http://openjdk.java.net/projects/nio/resources/AsynchronousIo.html
  3. http://www.ibm.com/developerworks/cn/linux/l-async/
  4. http://www.178linux.com/archives/4811
  5. Netty系列之Netty高性能之道
  6. Is Netty's Zero Copy different from OS level Zero Copy?
  7. Unix Network Programming关于I/O模型的经典介绍

Java AIO 编程相关推荐

  1. JAVA AIO编程

    Asynchronous IO: 异步非阻塞的编程方式 与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可.这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统 ...

  2. Java IO编程全解(五)——AIO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7794151.html 前面讲到:Java IO编程全解(四)--NIO编程 NIO2.0引入了新的异步通道的 ...

  3. NIO详解(一):java网络编程IO总结(BIO、NIO、AIO)

    1.基本概念 在Java网络通信中,最基本的概念就是Socket编程了.Socket又称"套接字" 向网络发出请求或者应答网络请求. Socket 和ServerSocket类库位 ...

  4. Java NIO AIO编程

    Java NIO & AIO编程 NIO 编程 AIO 编程 NIO 编程 NIO : Non - Blocking I/O:非阻塞I/O 一个线程,可以管理多个线程. 避免同步I/O通讯差的 ...

  5. python网络编程视频教程_Java网络开发视频教程 – 一站式学习Java网络编程视频教程 全面理解BIO(无密)...

    Java网络开发视频教程 – 一站式学习Java网络编程视频教程 全面理解BIO(无密) 全面理解BIO/NIO/AIO 网络层编程,是每一个开发者都要面对的技术.课程为解决大家学习网络层知识的难题, ...

  6. Java 核心编程技术干货

    Java 基础篇 Java 多线程篇 Java JVM篇 Java 进阶篇 Java 新特性篇 Java 工具类篇 Java 综合篇 Java基础篇 恕我直言,在座的各位根本写不好Java! 8张图带 ...

  7. Java AIO初探(异步网络IO)

    Java AIO初探(异步网络IO) 原文: http://www.blogjava.net/killme2008/archive/2009/09/20/295743.html 按照<Unix网 ...

  8. java 网络编程 方式_JAVA网络编程

    概念 BIO  阻塞io,1.4之前 NIO  no-blocking io 非阻塞io,jdk1.4 AIO  异步io,jdk1.7 浏览器输入网址,敲下回车之后发生了什么? 1.URL解析 2. ...

  9. Java BIO编程

    I/O 模型 1.I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能. (比如客户端和服务器端进行通信用的是单通道还是双通道,异步通信还是同步通信,是阻塞的 ...

最新文章

  1. ITOO4.1之缓存—分布式缓存Memcached学习(理论篇)
  2. 神策数据上线“点击分析”,深度感知用户点击行为
  3. linux手动同步文件命令,Linux文件同步命令rsync详解
  4. 面试突击32:为什么创建线程池一定要用ThreadPoolExecutor?
  5. 继涉黄被约谈 “比心陪练”App因内容涉宣扬暴力再被处罚
  6. Navicat连接虚拟机Linux的数据库时,出现错误代码“10038”
  7. 苹果7闪存速度测试软件,iphone6 plus闪存检测教程 iphone6检测tlc闪存图文步骤
  8. Android 存储
  9. mysql数据库文件怎么用_mysql数据库文件怎么用
  10. 豪越智慧后勤解决方案(教育/高校)
  11. 51单片机:定时器/计数器TMOD设定
  12. PHP环境配置遇到的问题与解决
  13. 几何分布的期望和方差公式推导_GPR(高斯过程回归)详细推导
  14. 苹果大战FBI,四个回合的波折之后有哪些启示?
  15. 李永乐数学基础过关660题线性代数填空题
  16. C# 使用NPOI批量导出
  17. 成都链安重磅出品 | 基于VS Code插件的智能合约自动形式化验证工具Beosin—VaaS『离线免费版』...
  18. 4.算法-所有数组的子集
  19. 如何查看git version多少(两种方法)
  20. Python 帮同事用pandas快速筛选Excel文件

热门文章

  1. requestPermissions读写手机存储权限_泛圈云盘可为企业建立高效安全的云办公在线协同文档存储?...
  2. [SpringSecurity]web权限方案_用户认证_设置用户名密码
  3. [Java基础]泛型基础
  4. Zookeeper理解---ZAB协议
  5. android圆形变方形动画,CSS3 简单的圆形/方形变形动画
  6. B-Donut Drone(循环/分块/DP)
  7. P4721 【模板】分治 FFT
  8. Codeforces Round #586 (Div. 1 + Div. 2) B. Multiplication Table 思维 + 公式
  9. cf600 E. Lomsat gelral
  10. 牛客题霸 [最长公共子序列] C++题解/答案