`
kuyuyingzi
  • 浏览: 53481 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Java NIO

 
阅读更多


NIO是New IO的缩写,顾名思义,是用于输入输出的新的API,那么,这个NIO相较于旧的IO有什么差别呢?

1、“阻塞”的通信机制

在原有的IO下,我们要与A进行通信时,会怎么做呢?先创建一个线程,然后建立连接,然后不断轮询等待接收消息。当需要与另一个B进行通信时,仍然先创建一个线程,然后建立连接(accept),不断轮询等待接收消息(read)… 在这种情况下,若通信的对象变多时,需要的线程就相应增长,并且每个线程都需要不断轮询等待,若没有消息接受时,则阻塞线程。由于消息传送的频率往往不高,所以,线程大部分时间都处于阻塞状态。而阻塞态的线程仍占用着系统资源,多个线程的管理及线程状态的切换(阻塞-就绪-运行)是比较耗时的。

由于这种方式在一个通信任务没有完成之前,是无法返回的,所以以上的这种方式称作“阻塞”的通信机制。

2、观察者模式

“阻塞”的通信机制,从本质上来看,就是每个通信的线程都需要询问“是否有我的数据?”,如果没有则将该线程阻塞,当有数据到来时,再唤醒线程。每个线程都在观察某个事物,等待自己的事件发生。这个描述,与观察者模式类似。我们可以使用观察者模式来进一步优化“阻塞”的通信机制。我们需要监听多个端口时,传统方式是建立多个线程进行监听。而现在,我们将每个端口封装为一个通道,所有传入该端口的数据都会出现在通道中,假设有3个需要监听的端口,则有3个通道,我们对这三个通道进行监听,使用Selector来注册我们对各个端口监听的行为。比如,对第一个端口(通道),需要监听他是否有可读数据,对于第二个端口(通道)需要监听他是否有accept请求… Selector记录了我们对各个通道的兴趣点,然后统一的对各个端口进行监听。当有相应的事件发生时,则调用相关的处理函数。这样,就完成了只用一个监听线程完成多个端口监听的任务。这种方式,就是“非阻塞”的通信机制。


3、nio 的关键

我们来想象一下读取文件的方式。首先,若直接从一个文件中读取数据,每次读取一个字符,若文件中有10个字符,那么就需要进行10次IO,而每一次IO都是很耗时的。为了减少IO的次数,我们使用了buffer,假定每次读取的数据都先存入buffer中,每次读取5个字符。那么,从buffer中间接读取字符,尽管同样是10次,但只需要2次的IO就够了,减少了IO次数,提高了效率。

为了能够完成端到端的传输,需要有一定的媒介进行。就像计算机系统中,数据传输是通过总线一样,nio中,数据的传输是通过“通道”进行的。例如,需要将文件A的内容传到(写入)文件B,那么,通过buffer与通道,可以如下操作:



在这里,通道作为中间的传输媒介,Buffer则附加在通道的两端,作为数据的“来源”与目标(真正的应该是File A与File B)。使用通道,则一方(File A)则只需要通过Buffer往通道里写,而另一方(File B)则只需要通过从通道里读即可,而无需关注二者的差异。

在NIO中,通道的一端绑定了相应的目的或者源头,例如文件、socket等,而另一端则是让使用buffer来获取或者写入数据。如下图:


缓冲区的工作与通道紧密联系。通道是I/O 传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标。

NIO中,有以下几个关键类型

1)缓存buffer

NIO中,提供了各个基本类型的buffer,提供给我们以不同的方式进行读写。例如可以用ByteBuffer进行字节(8bit为单位)的数据读写,使用CharByte进行字符(16bit为单位)的数据读写。

2)通道Channel

NIO中有以下几种通道:SocketChannel、ServerSocketChannel、DiagramChannel和FileChannel。

FileChannel只能通过FileOutputStream或者FileInputStream的getChannel调用得到,是单向的,即一端与相应的文件绑定,另一端进行write或者read。

ServerSocketChannel用于服务器端的通道创建,绑定了一个端口号后,所有接受的数据都会在该通道中,配合后面的Selector,可以直接获取封装好(SelectionKey)的某个客户发送的数据。

虽说通道既可读又可写,但实际上,一个绑定了某个源或者目的的通道,是只有读或写的功能的。

3)选择器Selector

他是使得多元I/O成为可能的关键,使用Selector来注册对通道的某些行为的关注,使之可以同时管理监控多个通道,当监控的时间发生时,唤醒并调用统一的事件处理函数进行相应。

选择器Selector:选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。当这么做的时候,可以选择将被激发的线程挂起,直到有就绪的的通道。

可选择通道SelectableChannel:这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。

选择键SelectionKey:选择键封装了特定的通道与特定的选择器的注册关系。选择键对象被SelectableChannel.register() 返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),指示了该注册关系所关心的通道操作,以及通道已经准备好的操作。

附上网上的一个NIO,Server和Client的代码:

  1. <spanstyle="font-size:18px">packagenio;
  2. importjava.io.IOException;
  3. importjava.net.InetSocketAddress;
  4. importjava.nio.ByteBuffer;
  5. importjava.nio.channels.SelectionKey;
  6. importjava.nio.channels.Selector;
  7. importjava.nio.channels.ServerSocketChannel;
  8. importjava.nio.channels.SocketChannel;
  9. importjava.util.Iterator;
  10. /**
  11. *NIO服务端
  12. *@author小路
  13. */
  14. publicclassNIOServer{
  15. //通道管理器
  16. privateSelectorselector;
  17. /**
  18. *获得一个ServerSocket通道,并对该通道做一些初始化的工作
  19. *@paramport绑定的端口号
  20. *@throwsIOException
  21. */
  22. publicvoidinitServer(intport)throwsIOException{
  23. //获得一个ServerSocket通道
  24. ServerSocketChannelserverChannel=ServerSocketChannel.open();
  25. //设置通道为非阻塞
  26. serverChannel.configureBlocking(false);
  27. //将该通道对应的ServerSocket绑定到port端口
  28. serverChannel.socket().bind(newInetSocketAddress(port));
  29. //获得一个通道管理器
  30. this.selector=Selector.open();
  31. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
  32. //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
  33. serverChannel.register(selector,SelectionKey.OP_ACCEPT);
  34. }
  35. /**
  36. *采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
  37. *@throwsIOException
  38. */
  39. @SuppressWarnings("unchecked")
  40. publicvoidlisten()throwsIOException{
  41. System.out.println("服务端启动成功!");
  42. //轮询访问selector
  43. while(true){
  44. //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
  45. System.out.println("开始阻塞");
  46. selector.select();
  47. System.out.println("解除阻塞");
  48. //获得selector中选中的项的迭代器,选中的项为注册的事件
  49. Iteratorite=this.selector.selectedKeys().iterator();
  50. while(ite.hasNext()){
  51. SelectionKeykey=(SelectionKey)ite.next();
  52. //删除已选的key,以防重复处理
  53. ite.remove();
  54. //客户端请求连接事件
  55. if(key.isAcceptable()){
  56. ServerSocketChannelserver=(ServerSocketChannel)key
  57. .channel();
  58. //获得和客户端连接的通道
  59. SocketChannelchannel=server.accept();
  60. //设置成非阻塞
  61. channel.configureBlocking(false);
  62. //在这里可以给客户端发送信息哦
  63. channel.write(ByteBuffer.wrap(newString("向客户端发送了一条信息")
  64. .getBytes()));
  65. //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
  66. channel.register(this.selector,SelectionKey.OP_READ);
  67. //获得了可读的事件
  68. }elseif(key.isReadable()){
  69. read(key);
  70. }
  71. }
  72. }
  73. }
  74. /**
  75. *处理读取客户端发来的信息的事件
  76. *@paramkey
  77. *@throwsIOException
  78. */
  79. publicvoidread(SelectionKeykey)throwsIOException{
  80. //服务器可读取消息:得到事件发生的Socket通道
  81. SocketChannelchannel=(SocketChannel)key.channel();
  82. //创建读取的缓冲区
  83. ByteBufferbuffer=ByteBuffer.allocate(1024);
  84. channel.read(buffer);
  85. byte[]data=buffer.array();
  86. Stringmsg=newString(data).trim();
  87. System.out.println("服务端收到信息:"+msg);
  88. ByteBufferoutBuffer=ByteBuffer.wrap(msg.getBytes());
  89. channel.write(outBuffer);//将消息回送给客户端
  90. }
  91. /**
  92. *启动服务端测试
  93. *@throwsIOException
  94. */
  95. publicstaticvoidmain(String[]args)throwsIOException{
  96. NIOServerserver=newNIOServer();
  97. server.initServer(8001);
  98. server.listen();
  99. }
  100. }
  101. </span>
  1. <spanstyle="font-size:18px">packagenio;
  2. importjava.io.IOException;
  3. importjava.net.InetSocketAddress;
  4. importjava.nio.ByteBuffer;
  5. importjava.nio.channels.SelectionKey;
  6. importjava.nio.channels.Selector;
  7. importjava.nio.channels.SocketChannel;
  8. importjava.util.Iterator;
  9. /**
  10. *NIO客户端
  11. *@author小路
  12. */
  13. publicclassNIOClient{
  14. //通道管理器
  15. privateSelectorselector;
  16. /**
  17. *获得一个Socket通道,并对该通道做一些初始化的工作
  18. *@paramip连接的服务器的ip
  19. *@paramport连接的服务器的端口号
  20. *@throwsIOException
  21. */
  22. publicvoidinitClient(Stringip,intport)throwsIOException{
  23. //获得一个Socket通道
  24. SocketChannelchannel=SocketChannel.open();
  25. //设置通道为非阻塞
  26. channel.configureBlocking(false);
  27. //获得一个通道管理器
  28. this.selector=Selector.open();
  29. //客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
  30. //用channel.finishConnect();才能完成连接
  31. channel.connect(newInetSocketAddress(ip,port));
  32. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
  33. channel.register(selector,SelectionKey.OP_CONNECT);
  34. }
  35. /**
  36. *采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
  37. *@throwsIOException
  38. */
  39. @SuppressWarnings("unchecked")
  40. publicvoidlisten()throwsIOException{
  41. //轮询访问selector
  42. while(true){
  43. selector.select();
  44. //获得selector中选中的项的迭代器
  45. Iteratorite=this.selector.selectedKeys().iterator();
  46. while(ite.hasNext()){
  47. SelectionKeykey=(SelectionKey)ite.next();
  48. //删除已选的key,以防重复处理
  49. ite.remove();
  50. //连接事件发生
  51. if(key.isConnectable()){
  52. SocketChannelchannel=(SocketChannel)key.channel();
  53. //如果正在连接,则完成连接
  54. if(channel.isConnectionPending()){
  55. channel.finishConnect();
  56. }
  57. //设置成非阻塞
  58. channel.configureBlocking(false);
  59. //在这里可以给服务端发送信息哦
  60. channel.write(ByteBuffer.wrap(newString("向服务端发送了一条信息")
  61. .getBytes()));
  62. //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
  63. channel.register(this.selector,SelectionKey.OP_READ);
  64. //获得了可读的事件
  65. }elseif(key.isReadable()){
  66. read(key);
  67. }
  68. }
  69. }
  70. }
  71. /**
  72. *处理读取服务端发来的信息的事件
  73. *@paramkey
  74. *@throwsIOException
  75. */
  76. publicvoidread(SelectionKeykey)throwsIOException{
  77. //和服务端的read方法一样
  78. //服务器可读取消息:得到事件发生的Socket通道
  79. SocketChannelchannel=(SocketChannel)key.channel();
  80. //创建读取的缓冲区
  81. ByteBufferbuffer=ByteBuffer.allocate(1024);
  82. channel.read(buffer);
  83. byte[]data=buffer.array();
  84. Stringmsg=newString(data).trim();
  85. System.out.println("服务端收到信息:"+msg);
  86. ByteBufferoutBuffer=ByteBuffer.wrap(msg.getBytes());
  87. }
  88. /**
  89. *启动客户端测试
  90. *@throwsIOException
  91. */
  92. publicstaticvoidmain(String[]args)throwsIOException{
  93. NIOClientclient=newNIOClient();
  94. client.initClient("localhost",8001);
  95. client.listen();
  96. }
  97. }
  98. </span>

原文链接:http://blog.csdn.net/burningsheep/article/details/12918189

分享到:
评论

相关推荐

    java NIO和java并发编程的书籍

    java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...

    JavaNIO chm帮助文档

    Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...

    java nio 包读取超大数据文件

    Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据...

    Java NIO英文高清原版

    Java NIO英文高清原版

    java nio中文版

    java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...

    java NIO 视频教程

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...

    Java NIO 中文 Java NIO 中文 Java NIO 中文文档

    Java NIO 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...

    java NIO 中文版

    讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用

    java nio 实现socket

    java nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socket

    Java NIO 英文文字版

    Many serious Java programmers, especially enterprise Java programmers, consider the new I/O API--called NIO for New Input/Output--the most important feature in the 1.4 version of the Java 2 Standard ...

    Java Nio selector例程

    java侧起server(NioUdpServer1.java),基于Java Nio的selector 阻塞等候,一个android app(NioUdpClient1文件夹)和一个java程序(UI.java)作为两个client分别向该server发数据,server收到后分别打印收到的消息...

    java NIO技巧及原理

    java NIO技巧及原理解析,java IO原理,NIO框架分析,性能比较

    java nio 读文件

    java nio 读文件,java nio 读文件

    java nio proraming pdf

    java.nio (NIO stands for non-blocking I/O) is a collection of Java programming language APIs that offer features for intensive I/O operations. It was introduced with the J2SE 1.4 release of Java by ...

    java NIO.zip

    java NIO.zip

    JAVA NIO 学习资料

    JAVA NIO学习资料JAVA NIO学习资料

    Java NIO测试示例

    Java NIO测试示例

    java nio入门学习,两个pdf

    java nio入门学习,两个pdfjava nio入门学习,两个pdf

    Java NIO.pdf

    java nio编程 非阻塞模式的通信 电子书 带目录标签

Global site tag (gtag.js) - Google Analytics