Java 新IO

时间:2023-03-09 07:41:10
Java 新IO
 NIO提供全新的底层I/O模型。与最初的java.io包中面向流(stream-oriented)概念不同,NIO采用了面向块的概念(block-oriented)。在尽可能的情况下,I/O的操作以大的数据块为单位进行,而不是一次一个字节或字符。这样提高java的I/O性能,也牺牲了操作的简单性。
  NIO中提供与平台无关的非阻塞I/O,与面向线程的、阻塞式I/O方式相比,多道通信、非阻塞I/O技术可以使应用程序更有效地处理大量连接的情况。
  新IO没有在原来的IO基础上开发,而是采用全新的类和接口,除了原有的功能外还提供了以下特性:
1. 多路选择的非*式I/O设施
2. 支持文件锁和内存映射
3. 支持正则表达式的模式匹配设施
4. 字符集编码器和译码器
缓冲区与Buffer
  在基本IO操作中所有的操作都是直接以流的形式完成的,而在NIO中所有的操作都要使用到缓冲区处理,且所有的读写操作都是通过缓冲区完成的。缓冲区(Buffer)是一个线性的、有序的数据集,只能容纳某种特定的数据类型。
java.nio.Buffer 是一个抽象类
public final int capacity()     返回此缓冲区的容量
public final int limit()     返回此缓冲区的限制
public final Buffer limit(int newLimit)     返回此缓冲区的限制
public final int position()     返回缓冲区的操作位置
public final Buffer position(int newPosition)     设置缓冲区的操作位置
public final Buffer clear()     清空缓冲区
public final Buffer flip()     重设缓冲区,在写入前调用,改变缓冲的指针
public final Buffer reset()     回复缓冲区中的标记位置
public final boolean hasRemaining()     判断在当前位置和限制之间是否有内容
nio中针对每一种基本数据类型都有一种对应的缓冲区操作类
java.nio.ByteBuffer
java.nio.CharBuffer
java.nio.ShortBuffer
java.nio.IntBuffer
java.nio.LongBuffer
java.nio.FloatBuffer
java.nio.DoubleBuffer
深入缓冲区操作
  Buffer 中存在一系列的状态变量,这些状态变量随着写入或读取都有可能被改变。用3个值表示缓冲区的状态
position 表示下一个缓冲区读取或写入的操作指针,当向缓冲区中写入数据时此指针就会改变,指针永远放到写入的最后一个元素之后。
limit 表示还有多少个数据需要存储或读取 position<=limit
capacity 表示缓冲区的最大容量<=capacity。 值在分配缓冲区时被设置,一般不会更改
通道(Channel)
  通道可以用来读取和写入数据,通道类似于之前的输入/输出流,但是程序不会直接操作通道,所有的内容都是先读到或写入到缓冲区中,再通过缓冲区中取得或写入的。
  传统的流操作分输入或输出流,而通道本身是双向操作的,既可以完成输入也可以完成输出。
Channel本身是一个接口
void close()     关闭通道
boolean isOpen()     判断此通道是否是打开的
FileChannel 类
 进行文件的读/写操作
public int read(ByteBuffer dst)     将内容读入到缓冲区中
public int write(ByteBuffer src)     将内容从缓冲区写入到通道
public final void close()     关闭通道
public abstract MappedByteBuffer map(FileChannel,.MapMode mode, long position, long size)     将通道的文件区域映射到内容中,同时指定映射模式、文件中的映射文件以及要映射的区域大小
  要使用FileChannel,可以依靠FileInputStream或FileOutputStream类中的getChannel()方法取得输入或输出的通道。
内存映射
  内存映射可以把文件映射到内存中,这样文件内的数据就可以用内存读/写指令来访问,而不是用InputStream或OutputStream这样的I/O操作类,这也是速度最快的读取文件方式。
  使用FileChannel类提供的map()方法,可以将文件映射到内存中。
public abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
map()方法在使用时要指定映射模式,在内存映射中提供了3种模式,此3种模式分别有FileChannel类中的3个常量表示
public static final FileChannel.MapMode READ_ONLY     只读映射模式
public static final FileChannel.MapMode Read_WRITE     读取/写入映射模式
public static final FileChannel.MapMode PRIVATE     专用(写入时复制)映射模式
文件锁:FileLock
  Java 新IO中提供了文件锁的功能,这样当一个线程将文件锁定后,其他线程是无法操作此文件的。要想进行文件的锁定操作,则要使用FileLock类完成,此类的对象需要依靠FileChannel进行实例化操作。
FileChannel类中提供的实例化FileLock对象的方法
public final FileLock lock()     获得此通道文件的独占锁定
public abstract FileLock lock(long position, long size, boolean shared)     获得此通道文件给定区域的锁定,并指定锁定位置、锁定大小、是共享锁定true或独占锁定false
public final FileLock tryLock()     试图获取此通道的独占锁定
public abstract FileLock tryLock(long position, long size, boolean shared)     试图获取此通道指定区域的锁定,并指定锁定位置、锁定大小,属于共享锁定或独占锁定
共享锁:允许多个线程进行文件的读取操作
独占锁:只允许一个线程进行文件的读/写操作
文件锁定后需要依靠FileLock类进行解锁,FileLock类的常用方法:
public final boolean isShared()     判断锁定是否为共享锁定
public final FileChannel channel()     返回此锁定的FileChannel
public abstract void release()     释放锁定(解锁)
public final long size()     返回锁定区域的大小
Charset 字符集
  负责处理编码的问题,包含了创建编码器(CharsetEncoder)和创建解码器(CharsetDecoder)的操作
Selector
  在原来使用IO和Socket构造网络服务时,所有的网络服务将使用阻塞的方式进行客户端的连接,而如果使用新IO则可以构造一个非阻塞的网络服务。
Selector类的常用方法:
public static Selector open()     打开一个选择器
public abstract int select()     选择一组键,通道已经为IO做好准备
public abstract Set<SelectionKey> selectedKeys()     返回此选择器已选择的key
  在进行非阻塞网络开发时需要使用SelectableChannel类向Select类注册,而且在新IO中实现网络程序需要依靠ServerSocketChannel与SocketChannel类,这两个都是SelectableChannel的子类,SelectableChannel提供了注册Selector的方法和阻塞模式
ServerSocketChannel类的常用方法:
public abstract SelectableChannel configBlocking(boolean block)     调整此通道的阻塞模式,如果为true将被设置为阻塞模式,如果为false将被设置为非阻塞模式
public final SelectionKey register(Selector sel, int ops)     向指定的选择器注册通道并设置Selector域,返回一个选择键
public static ServerSocketChannel open()     打开服务器的套接字通道
public abstract ServerSocket socket()     返回与此通道关联的服务器套接字
  在使用register()方法时需要指定一个选择器(Selector对象)以及Select域,Selector对象可以通过Selector中的open()方法取得,而Selector域则在SelectionKey类中定义
4种Selector域
public static final int OP_ACCEPT     相当于ServerSocket中的accept()操作
public static final int OP_CONNECT     连接操作
public static final int OP_READ     读操作
public static final int OP_WRITE     写操作
建立一个非阻塞的服务器端:
  首先通过Selector.open()方法打开一个选择器,之后通过ServerSocketChannel类中的open()方法打开一个服务器套接字通道。最重要的是configureBlocking()方法,将服务器设置为非阻塞状态,之后通过ServerSocketChannel的socket()方法返回一个ServerSocket对象,并在8888端口绑定服务器的监听端口。程序的最后使用register()方法将选择器注册为accept方式,等待客户端连接。
  如果要使用服务器想客户端发送信息,则需要通过SelectionKey类中提供的方法判断服务器的操作状态。而要想取得客户端的连接也需要使用SelectionKey类