`
wufan0023
  • 浏览: 29159 次
  • 性别: Icon_minigender_1
  • 来自: hefei
社区版块
存档分类
最新评论

Java NIO API详解 (I)[z]

阅读更多

NIO API主要集中在java.nio和它的subpackages中:

java.nio

定义了Buffer及其数据类型相关的子类。其中被java.nio.channels中的类用来进行IO操作的ByteBuffer的作用非常重要。

 

java.nio.channels

定义了一系列处理IOChannel接口以及这些接口在文件系统和网络通讯上的实现。通过Selector这个类,还提供了进行异步IO操作的办法。这个包可以说是NIO API的核心。

 

java.nio.channels.spi

定义了可用来实现channelselector API的抽象类。

 

java.nio.charset

       定义了处理字符编码和解码的类。

 

java.nio.charset.spi

       定义了可用来实现charset API的抽象类。

 

java.nio.channels.spijava.nio.charset.spi这两个包主要被用来对现有NIO API进行扩展,在实际的使用中,我们一般只和另外的3个包打交道。下面将对这3个包一一介绍。

 

Package java.nio

这个包主要定义了Buffer及其子类。Buffer定义了一个线性存放primitive type数据的容器接口。对于除boolean以外的其他primitive type,都有一个相应的Buffer子类,ByteBuffer是其中最重要的一个子类。

 

下面这张UML类图描述了java.nio中的类的关系:

 


 

Buffer

定义了一个可以线性存放primitive type数据的容器接口。Buffer主要包含了与类型(byte, char…)无关的功能。值得注意的是Buffer及其子类都不是线程安全的。

 

每个Buffer都有以下的属性:

 

capacity

这个Buffer最多能放多少数据。capacity一般在buffer被创建的时候指定。

limit

Buffer上进行的读写操作都不能越过这个下标。当写数据到buffer中时,limit一般和capacity相等,当读数据时,limit代表buffer中有效数据的长度。

position

/写操作的当前下标。当使用buffer的相对位置进行读/写操作时,读/写会从这个下标进行,并在操作完成后,buffer会更新下标的值。

mark

一个临时存放的位置下标。调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设置为mark的值。mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。

 

这些属性总是满足以下条件

0 <= mark <= position <= limit <= capacity

 

limitposition的值除了通过limit()position()函数来设置,也可以通过下面这些函数来改变:

 

Buffer clear()

position设为0,把limit设为capacity,一般在把数据写入Buffer前调用。

Buffer flip()

limit设为当前position,把position设为0,一般在从Buffer读出数据前调用。

Buffer rewind()

position设为0limit不变,一般在把数据重写入Buffer前调用。

 

Buffer对象有可能是只读的,这时,任何对该对象的写操作都会触发一个ReadOnlyBufferExceptionisReadOnly()方法可以用来判断一个Buffer是否只读。

 

ByteBuffer

Buffer的子类中,ByteBuffer是一个地位较为特殊的类,因为在java.io.channels中定义的各种channelIO操作基本上都是围绕ByteBuffer展开的。

 

 

ByteBuffer定义了4static方法来做创建工作

 

ByteBuffer allocate(int capacity)

创建一个指定capacityByteBuffer

ByteBuffer allocateDirect(int capacity)

创建一个directByteBuffer,这样的ByteBuffer在参与IO操作时性能会更好(很有可能是在底层的实现使用了DMA技术),相应的,创建和回收directByteBuffer的代价也会高一些。isDirect()方法可以检查一个buffer是否是direct的。

ByteBuffer wrap(byte [] array)

ByteBuffer wrap(byte [] array, int offset, int length)

把一个byte数组或byte数组的一部分包装成ByteBuffer

 

ByteBuffer定义了一系列getput操作来从中读写byte数据,如下面几个:

 

byte get()

ByteBuffer get(byte [] dst)

byte get(int index)

 

ByteBuffer put(byte b)

ByteBuffer put(byte [] src)

ByteBuffer put(int index, byte b)

 

这些操作可分为绝对定位和相对定为两种,相对定位的读写操作依靠position来定位Buffer中的位置,并在操作完成后会更新position的值。

在其它类型的buffer中,也定义了相同的函数来读写数据,唯一不同的就是一些参数和返回值的类型。

 

除了读写byte类型数据的函数,ByteBuffer的一个特别之处是它还定义了读写其它primitive数据的方法,如:

 

int getInt()

       ByteBuffer中读出一个int值。

ByteBuffer putInt(int value)

       写入一个int值到ByteBuffer中。

 

读写其它类型的数据牵涉到字节序问题,ByteBuffer会按其字节序(大字节序或小字节序)写入或读出一个其它类型的数据(int,long…)。字节序可以用order方法来取得和设置:

 

ByteOrder order()

       返回ByteBuffer的字节序。

ByteBuffer order(ByteOrder bo)

       设置ByteBuffer的字节序。

 

ByteBuffer另一个特别的地方是可以在它的基础上得到其它类型的buffer。如:

 

CharBuffer asCharBuffer()

为当前的ByteBuffer创建一个CharBuffer的视图。在该视图buffer中的读写操作会按照ByteBuffer的字节序作用到ByteBuffer中的数据上。

 

用这类方法创建出来的buffer会从ByteBufferposition位置开始到limit位置结束,可以看作是这段数据的视图。视图bufferreadOnly属性和direct属性与ByteBuffer的一致,而且也只有通过这种方法,才可以得到其他数据类型的direct buffer

 

ByteOrder

用来表示ByteBuffer字节序的类,可将其看成java中的enum类型。主要定义了下面几个static方法和属性:

 

ByteOrder BIG_ENDIAN

       代表大字节序的ByteOrder

ByteOrder LITTLE_ENDIAN

       代表小字节序的ByteOrder

ByteOrder nativeOrder()

       返回当前硬件平台的字节序。

 

MappedByteBuffer

ByteBuffer的子类,是文件内容在内存中的映射。这个类的实例需要通过FileChannelmap()方法来创建。

 

 

接下来看看一个使用ByteBuffer的例子,这个例子从标准输入不停地读入字符,当读满一行后,将收集的字符写到标准输出:

    public static void main(String [] args) throws IOException {

       // 创建一个capacity为256的ByteBuffer

       ByteBuffer buf = ByteBuffer.allocate(256);

       while (true) {

           // 从标准输入流读入一个字符

           int c = System.in.read();

           // 当读到输入流结束时,退出循环

           if (c == -1)

              break;          

           // 把读入的字符写入ByteBuffer中

           buf.put((byte) c);

           // 当读完一行时,输出收集的字符

           if (c == '\n') {

              // 调用flip()使limit变为当前的position的值,position变为0,

              // 为接下来从ByteBuffer读取做准备

              buf.flip();

              // 构建一个byte数组

              byte [] content = new byte[buf.limit()];

              // 从ByteBuffer中读取数据到byte数组中

              buf.get(content);

              // 把byte数组的内容写到标准输出

              System.out.print(new String(content));

              // 调用clear()使position变为0,limit变为capacity的值,

              // 为接下来写入数据到ByteBuffer中做准备

              buf.clear();

           }

       }

    }

Package java.nio.channels

这个包定义了Channel的概念,Channel表现了一个可以进行IO操作的通道(比如,通过FileChannel,我们可以对文件进行读写操作)。java.nio.channels包含了文件系统和网络通讯相关的channel类。这个包通过SelectorSelectableChannel这两个类,还定义了一个进行异步(non-blockingIO操作的API,这对需要高性能IO的应用非常重要。

 

下面这张UML类图描述了java.nio.channelsinterface的关系:

 

 

Channel

Channel表现了一个可以进行IO操作的通道,该interface定义了以下方法:

 

boolean isOpen()

       Channel是否是打开的。

void close()

       关闭这个Channel,相关的资源会被释放。

 

ReadableByteChannel

定义了一个可从中读取byte数据的channel interface

 

int read(ByteBuffer dst)

channel中读取byte数据并写到ByteBuffer中。返回读取的byte数。

 

WritableByteChannel

定义了一个可向其写byte数据的channel interface

 

int write(ByteBuffer src)

       ByteBuffer中读取byte数据并写到channel中。返回写出的byte数。

 

ByteChannel

ByteChannel并没有定义新的方法,它的作用只是把ReadableByteChannelWritableByteChannel合并在一起。

 

ScatteringByteChannel

继承了ReadableByteChannel并提供了同时往几个ByteBuffer中写数据的能力。

 

GatheringByteChannel

继承了WritableByteChannel并提供了同时从几个ByteBuffer中读数据的能力。

 

InterruptibleChannel

用来表现一个可以被异步关闭的Channel。这表现在两方面:

1.    当一个InterruptibleChannelclose()方法被调用时,其它block在这个InterruptibleChannelIO操作上的线程会接收到一个AsynchronousCloseException

2.    当一个线程blockInterruptibleChannelIO操作上时,另一个线程调用该线程的interrupt()方法会导致channel被关闭,该线程收到一个ClosedByInterruptException,同时线程的interrupt状态会被设置。

 

 

接下来的这张UML类图描述了java.nio.channels中类的关系:

 

 

异步IO

异步IO的支持可以算是NIO API中最重要的功能,异步IO允许应用程序同时监控多个channel以提高性能,这一功能是通过SelectorSelectableChannelSelectionKey3个类来实现的

 

SelectableChannel代表了可以支持异步IO操作的channel,可以将其注册在Selector,这种注册的关系由SelectionKey这个类来表现(见UML图)。Selector这个类通过select()函数,给应用程序提供了一个可以同时监控多个IO channel的方法:

 

应用程序通过调用select()函数,让Selector监控注册在其上的多个SelectableChannel,当有channelIO操作可以进行时,select()方法就会返回以让应用程序检查channel的状态,并作相应的处理。 

下面是JDK 1.4中异步IO的一个例子,这段code使用了异步IO实现了一个time server

   private static void acceptConnections(int port) throws Exception {

       // 打开一个Selector

       Selector acceptSelector = SelectorProvider.provider().openSelector();


       // 创建一个ServerSocketChannel,这是一个SelectableChannel的子类

       ServerSocketChannel ssc = ServerSocketChannel.open();

       // 将其设为non-blocking状态,这样才能进行异步IO操作

       ssc.configureBlocking(false);


       // 给ServerSocketChannel对应的socket绑定IP和端口

       InetAddress lh = InetAddress.getLocalHost();

       InetSocketAddress isa = new InetSocketAddress(lh, port);

       ssc.socket().bind(isa);


       // 将ServerSocketChannel注册到Selector上,返回对应的SelectionKey

       SelectionKey acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);


       int keysAdded = 0;


       // 用select()函数来监控注册在Selector上的SelectableChannel

       // 返回值代表了有多少channel可以进行IO操作 (ready for IO)

       while ((keysAdded = acceptSelector.select()) > 0) {

           // selectedKeys()返回一个SelectionKey的集合,

           // 其中每个SelectionKey代表了一个可以进行IO操作的channel。

           // 一个ServerSocketChannel可以进行IO操作意味着有新的TCP连接连入了

           Set readyKeys = acceptSelector.selectedKeys();

           Iterator i = readyKeys.iterator();


           while (i.hasNext()) {

              SelectionKey sk = (SelectionKey) i.next();

              // 需要将处理过的key从selectedKeys这个集合中删除

              i.remove();

              // 从SelectionKey得到对应的channel

              ServerSocketChannel nextReady = (ServerSocketChannel) sk.channel();

              // 接受新的TCP连接

              Socket s = nextReady.accept().socket();

              // 把当前的时间写到这个新的TCP连接中

              PrintWriter out = new PrintWriter(s.getOutputStream(), true);

              Date now = new Date();

              out.println(now);

              // 关闭连接

              out.close();

           }

       }

    }

 

 

这是个纯粹用于演示的例子,因为只有一个ServerSocketChannel需要监控,所以其实并不真的需要使用到异步IO。不过正因为它的简单,可以很容易地看清楚异步IO是如何工作的。

分享到:
评论

相关推荐

    java.nio API详解

    在JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的同步(blocking)API。对于大多数应用来说,这样的API使用很方便,然而,一些对性能要求较高的应用,尤其是服务端应用,往往需要一个更为有效的方式来...

    NIO开发API详解

    详细介绍NIO技术

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

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

    Java NIO详解及源码下载

    博客地址:http://blog.csdn.net/u010156024/article/details/44310709 欢迎访问。 代码中详细讲述了Java io流和nio流的用法,可以参考学习。

    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系列教程(六)...

    JavaNIO_API帮助文档详解

    JavaNIO_API帮助文档详解,对于开发NIO很有帮助

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

    Java NIO英文高清原版

    Java NIO英文高清原版

    java nio 包读取超大数据文件

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

    Java NIO详解(学习资料)

    Java NIO详解

    java NIO.zip

    java NIO.zip

    Java_NIO_API详解[参照].pdf

    Java_NIO_API详解[参照].pdf

    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_API详解

    NIO的Api的详细讲解,非常详细,对方法和参数的作用就解释的非常清楚。

    JavaNIO.pdf

    Java NIO(Non-blocking I/O)是Java平台中的一种I/O处理方式,它提供了面向缓冲区的I/O处理机制,可以实现高性能、高效的I/O操作。 缓冲区(Buffer) 缓冲区是Java NIO中非常重要的一个概念,它是特定基本类型...

    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收到后分别打印收到的消息...

Global site tag (gtag.js) - Google Analytics