标签:cal java io strong 获取 内存映射文件 out 应用 文件流 ima
Java NIO
NIO (JDK1.5 new IO) 主要有三大核心部分:
传统IO 基于 字节流 和 字符流进行操作,而 NIO 基于Channel 和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
NIO 和 传统IO(一下简称IO)之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
IO的各种流是阻塞的。这意味着,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变得可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(Channel)。
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。如图所示:
Channel 和 IO中的 Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream。 而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
NIO中的Channel的主要实现有:
Channel 和 Buffer 有好几种类型。下面是JAVA NIO中的一些主要 Channel 的实现:
以下是Java NIO里关键的 Buffer 实现(Buffer的类型):
这些Buffer覆盖了你能通过IO发送的基本数据类型:byte、short、 int、 long、 float、 double 和 char。
Java NIO 还有个 MappedByteBuffer,用于表示内存映射文件。后面会讲到。
Selector 运行单线程处理多个Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用Selector就会很方便。例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel,然后调用它的 select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。
二、FileChannel (文件IO)
传统IO 与 FileChannel (NIO) 读取文件并输出代码比较:
传统:IO
public static void Io(){
InputStream in = null;
try{
in = new BufferedInputStream(new FileInputStream("src/nomal_io.txt"));
byte [] buf = new byte[1024];
// 读取数据到缓冲区
int bytesRead = in.read(buf);
while(bytesRead != -1)
{
for(int i=0;i<bytesRead;i++)
System.out.print((char)buf[i]);
bytesRead = in.read(buf);
}
}catch (IOException e)
{
e.printStackTrace();
}finally{
try{
if(in != null){
in.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
NIO :
public static void Nio(){
RandomAccessFile aFile = null;
try{
aFile = new RandomAccessFile("src/nio.txt","rw");
FileChannel fileChannel = aFile.getChannel();
// 创建 1024 字节的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
// 从通道中读取数据到 缓冲区中
int bytesRead = fileChannel.read(buf);
System.out.println(bytesRead);
while(bytesRead != -1)
{
// 切换缓冲区的模式为读模式
buf.flip();
// 当缓冲区 buf 中有数据则一直循环
while(buf.hasRemaining())
{
System.out.print((char)buf.get());
}
// 清空缓存区
buf.compact();
// 继续读取文件流到缓冲区中, 返回读取的字节数
bytesRead = fileChannel.read(buf);
}
}catch (IOException e){
e.printStackTrace();
}finally{
try{
if(aFile != null){
aFile.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
可以看出,使用 NIO 的实现方式相对复杂一点。
使用Buffer读写数据一般遵循以下几个步骤:
1、分配空间(分配1024 字节capacity ByteBuffer buf = ByteBuffer.allocate(1024); )。
2、写入数据到 Buffer:
3、调用 flip()
方法,将Buffer从写模式切换到读模式。
4、从 Buffer 中读取数据:
5、调用 clear()
方法或者 compact()
方法。 一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
Buffer 的其他方法:
Buffer.rewind() 将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。
一旦读完Buffer中的数据,需要让Buffer准备好再次被写入。可以通过clear()或compact()方法来完成。
如果调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。
compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。例如:
buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset(); //set position back to mark.
标签:cal java io strong 获取 内存映射文件 out 应用 文件流 ima
原文地址:https://www.cnblogs.com/dw3306/p/14920930.html