zoukankan      html  css  js  c++  java
  • Java NIO使用及原理分析 (一)

    在Java1.4之前的I/O系统中,提供的都是面向流的I/O系统,系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的数据,面向流的I/O速度非常慢,而在Java 1.4中推出了NIO,这是一个面向块的I/O系统,系统以块的方式处理处理,每一个操作在一步中产生或者消费一个数据库,按块处理要比按字节处理数据快的多。

    在NIO中有几个核心对象需要掌握:缓冲区(Buffer)、通道(Channel)、选择器(Selector)。

    缓冲区Buffer

    缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。

    在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer,对于Java中的基本类型,基本都有一个具体Buffer类型与之相对应,它们之间的继承关系如下图所示:

    下面是一个简单的使用IntBuffer的例子:

    1. import java.nio.IntBuffer;  
    2.   
    3. public class TestIntBuffer {  
    4.     public static void main(String[] args) {  
    5.         // 分配新的int缓冲区,参数为缓冲区容量  
    6.         // 新缓冲区的当前位置将为零,其界限(限制位置)将为其容量。它将具有一个底层实现数组,其数组偏移量将为零。  
    7.         IntBuffer buffer = IntBuffer.allocate(8);  
    8.   
    9.         for (int i = 0; i < buffer.capacity(); ++i) {  
    10.             int j = 2 * (i + 1);  
    11.             // 将给定整数写入此缓冲区的当前位置,当前位置递增  
    12.             buffer.put(j);  
    13.         }  
    14.   
    15.         // 重设此缓冲区,将限制设置为当前位置,然后将当前位置设置为0  
    16.         buffer.flip();  
    17.   
    18.         // 查看在当前位置和限制位置之间是否有元素  
    19.         while (buffer.hasRemaining()) {  
    20.             // 读取此缓冲区当前位置的整数,然后当前位置递增  
    21.             int j = buffer.get();  
    22.             System.out.print(j + "  ");  
    23.         }  
    24.   
    25.     }  
    26.   
    27. }  
    运行后可以看到:

    在后面我们还会继续分析Buffer对象,以及它的几个重要的属性。

    通道Channel

    通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

    在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。它们之间的继承关系如下图所示:

    使用NIO读取数据

    在前面我们说过,任何时候读取数据,都不是直接从通道读取,而是从通道读取到缓冲区。所以使用NIO读取数据可以分为下面三个步骤: 
    1. 从FileInputStream获取Channel 
    2. 创建Buffer 
    3. 将数据从Channel读取到Buffer中

    下面是一个简单的使用NIO从文件中读取数据的例子:

    1. import java.io.*;  
    2. import java.nio.*;  
    3. import java.nio.channels.*;  
    4.   
    5. public class Program {  
    6.     static public void main( String args[] ) throws Exception {  
    7.         FileInputStream fin = new FileInputStream("c:\test.txt");  
    8.           
    9.         // 获取通道  
    10.         FileChannel fc = fin.getChannel();  
    11.           
    12.         // 创建缓冲区  
    13.         ByteBuffer buffer = ByteBuffer.allocate(1024);  
    14.           
    15.         // 读取数据到缓冲区  
    16.         fc.read(buffer);  
    17.           
    18.         buffer.flip();  
    19.           
    20.         while (buffer.remaining()>0) {  
    21.             byte b = buffer.get();  
    22.             System.out.print(((char)b));  
    23.         }  
    24.           
    25.         fin.close();  
    26.     }  
    27. }  

    使用NIO写入数据

    使用NIO写入数据与读取数据的过程类似,同样数据不是直接写入通道,而是写入缓冲区,可以分为下面三个步骤: 
    1. 从FileInputStream获取Channel 
    2. 创建Buffer 
    3. 将数据从Channel写入到Buffer中

    下面是一个简单的使用NIO向文件中写入数据的例子:

    1. import java.io.*;  
    2. import java.nio.*;  
    3. import java.nio.channels.*;  
    4.   
    5. public class Program {  
    6.     static private final byte message[] = { 8311110910132,  
    7.         9812111610111546 };  
    8.   
    9.     static public void main( String args[] ) throws Exception {  
    10.         FileOutputStream fout = new FileOutputStream( "c:\test.txt" );  
    11.           
    12.         FileChannel fc = fout.getChannel();  
    13.           
    14.         ByteBuffer buffer = ByteBuffer.allocate( 1024 );  
    15.           
    16.         for (int i=0; i<message.length; ++i) {  
    17.             buffer.put( message[i] );  
    18.         }  
    19.           
    20.         buffer.flip();  
    21.           
    22.         fc.write( buffer );  
    23.           
    24.         fout.close();  
    25.     }  
    26. }  

    本文介绍了Java NIO中三个核心概念中的两个,并且看了两个简单的示例,分别是使用NIO进行数据的读取和写入,Java NIO中最重要的一块Nonblocking I/O将在第三篇中进行分析,下篇将会介绍Buffer内部实现。


  • 相关阅读:
    LOJ 6089 小Y的背包计数问题 —— 前缀和优化DP
    洛谷 P1969 积木大赛 —— 水题
    洛谷 P1965 转圈游戏 —— 快速幂
    洛谷 P1970 花匠 —— DP
    洛谷 P1966 火柴排队 —— 思路
    51Nod 1450 闯关游戏 —— 期望DP
    洛谷 P2312 & bzoj 3751 解方程 —— 取模
    洛谷 P1351 联合权值 —— 树形DP
    NOIP2007 树网的核
    平面最近点对(加强版)
  • 原文地址:https://www.cnblogs.com/marcotan/p/4256979.html
Copyright © 2011-2022 走看看