Java基础笔记之NIO

来源:互联网 发布:最优化计算方法课后题 编辑:程序博客网 时间:2024/05/17 00:07

1.  Java NIO概述

        Java中的输入流/输出流都是阻塞式的输入/输出,例如InputStream的read()方法从流中读取数据时,如果数据源中没有数据,它也会阻塞该线程。而且传统的输入流、输出流都是通过字节的移动来处理的,因此面向流的输入/输出系统一次只能处理一个字节,因此面向流的输入/输出系统效率不高。

        新IO和传统的IO有相同的目的,都是用于进行输入/输出,但新IO使用了不同的方式来处理输入/输出,新IO采用内存映射文件的方式来处理输入/输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(实际上这种方式模拟了操作系统上的虚拟内存的概念)通过这种方式来进行输入/输出比传统的输入/输出要快得多。

Java中与新IO相关的包如下。

java.nio包:主要包含各种与Buffer相关的类。

java.nio.channels包:主要包含与Channel和Selector相关的类。

java.nio.charset包:主要包含与字符集相关的类。

java.nio.channels.spi包:主要包含与Channel相关的服务提供者编程接口。

java.nio.charset.spi包:主要包含与字符集先关的服务提供者编程接口。

Channel和Buffer是新IO中两个核心对象。

Channel是对传统的输入/输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输;Channel提供map()方法直接将一块数据映射到内存中,因此我们可以这样理解,传统的IO系统是面向流的处理,则新IO是面向块的处理。

Buffer是一个容器,本质上是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先放到Buffer中。


2.  使用Buffer

Buffer是一个抽象类,其子类有ByteBuffer(最常用)、CharBuffer(常用)、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。

Buffer的所有子类都没构造器,只能通过以下静态方法来获取实例对象:

static XxxBuffer allocate(int capacity):创建一个容量为capacity的XxxBuffer对象。

Buffer中3个重要的概念:容量(capacity)、界限(limit)、位置(position)

容量(capacity):缓冲区的容量表示该Buffer的最大数据容量,表示最多可以存储多少数据。不允许为负值,创建后不能修改。

界限(limit):第一个不应该被读出的或者写入的缓冲区位置索引。位于limit后的数据既不可被读,也不可写。

位置(position):用于指明下一个可以被读出的或者写入的缓冲区位置索引。

因此,position~limit中数据是可被读写的,而之外的位置是不允许读写的。

Buffer中两个重要的方法:

flip():为从Buffer中取出数据做好准备,该方法将limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移动了开始位置。

clear():为再次向Buffer中装入数据做好准备,该方法将position置为0,将limit置为capacity。


3.  使用Channel

Channel类与传统的流对象两个主要区别:

Channel可以直接将制定文件的部分或全部直接映射成Buffer。

程序不能直接访问Channel中的数据,包括读取、写入都不行,Channel只能与Buffer进行交互。也就是说,如果要从Channel中取得数据,必须先从Buffer中取出一些数据,然后让程序从Buffer中取出这些数据;如果要将程序中的数据写入Channel,一样先将程序中的数据写入Buffer,程序再将Buffer里的数据写入Channel中。

Channel提供了多种实现类:

FileChannel是用于处理文件的管道。

Pipe.SinkChannel、Pipe.SourceChannel是用于支持线程之间通信的管道Channel。

ServerSocketChannel、SocketChannel是用于支持TCP网络通信的Channel。

DatagramChannel是用于支持UDP网络通信的Channel。

获取Channel对象:

所有的Channel都不能通过构造器来创建实例对象,而是通过传统的节点InputStream、OutputStream的getChannel()方法来返回对应的Channel,不同的节点流对象的Channel对象都是不一样的。

Channel中三个重要的方法:

map()方法用于将Channel对应的部分或全部数据映射成ByteBuffer;

read()和write()的一系列重载的方法用于从Buffer中读取数据或向Buffer中写入数据。

map()方法的签名:MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)

MappedByteBuffer为ByteBuffer的子类。

mode:执行映射时的模式,分别为只读、读写等模式。

position、size:用于控制将Channel的哪些数据映射成ByteBuffer。


4. 文件锁

        文件锁可以有效地阻止多个进程并发修改同一个文件,在Java NIO中提供FileLock来支持文件锁定功能。在FileChannel中提供lock()/tryLock()方法可以获得文件锁FileLock对象,从而锁定文件。下面是两个方法的区别:

        lock()方法试图锁定某个文件时,如果无法得到文件锁,程序将一直阻塞;

        而tryLock()方法是尝试锁定文件,它将直接返回而不是阻塞,如果获得了文件锁,该方法则返回该文件锁,否则返回NULL。

        如果FileChannel只想锁定文件的部分内容,而不是锁定全部内容,则可以使用如下的lock()/tryLock()重载方法。

                lock( long position, long size, boolean shared ):对文件从position开始,长度为size的内容加锁,该方法是阻塞的。

                tryLock( long position, long size, boolean shared ):非阻塞方法,功能与lock()方法一样。

        当shared为true时,表明该锁是一个共享锁,它将允许多个进程来读取文件,但不允许其他进程将其设为排他锁。当shared为false的时,表明这是一个排他锁。

        处理完成之后,调用FileLock的release()方法释放文件锁。





0 0
原创粉丝点击