java NIO(Channel)

1. NIO与IO的区别

NIO(New IO/Non-Blocking IO)是支持面向缓冲区、基于通道的IO操作。将以更高的效率进行文件的IO操作,可以代替原来的IO操作。

IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO
———— Selector(多路复用器、选择器)

2. 通道与缓冲区

通道是建立一个程序与资源之间的通路,即表示打开到IO设备(文件、套接字)的连接,不能通过通道直接操作资源(文件),NIO是以块来操作资源的,所以要借助缓冲区来实现。

通道负责传输,缓冲区负责存储数据,即当做数据的中转站。

3. 缓冲区

分为直接缓冲区,和间接缓冲区。

间接缓冲区:在JVM的堆上建立的缓冲区。

直接缓冲区:直接在系统内存上建立的缓冲区。

4. 通道基础

4.1. 通过与系统内存交互示意图

4.2. 通道的主要实现

  • 需要实现的接口: java.nio.channels.Channel

  • 声明:

`public interface Channel

extends Closeable`

  • 实现该接口的常用的类:
  • 磁盘IO:FileChannel

  • 网络IO:

  • SocketChannel(普通套接字通道[TCP])

  • ServerSocketChannel(服务器套接字通道[TCP])

  • DatagramChannel (UDP套接字通道)

5. 通道获取与使用

5.1. 获取通道

  • 对支持通道的类提供getChannel方法
  • 本地IO:FileInputStreamFileOutputStreamRandomAccessFile

  • 网络IO:SocketServerSocketDatagramSocket

  • 各个通道提供了一个静态方法open
  • static FileChannel open(Path path, OpenOption... options)

  • static SocketChannel open(SocketAddress remote) / static SocketChannel open()

  • static ServerSocketChannel open()

  • static DatagramChannel open() / static DatagramChannel open(ProtocolFamily family)

  • 通过Files工具类获取
  • static SeekableByteChannel newByteChannel(Path path, OpenOption... options)

  • 其中SeekableByteChannel是FileChannel的父类

5.2. 通道使用

利用通道完成文件复制

5.2.1. 利用间接缓冲区完成


public void tets1() throws Exception {

    //获取输入输出的通道

    FileChannel fin = FileInputStream("1.txt").getChannel();

    FileChannel fout = FileOutputStream("2.txt").getChannel();


    //分配指定大小的缓冲区,间接缓冲区

    ByteBuffer buf = ByteBuffer.allocate(1024);


    //将数据存入缓冲区

    while(fin.read(buf) != -1){

    //切换到读模式

    buf.flip();

    //写入

    fout.write(buf);

    //重置缓冲区,便于下次使用

    buf.clear();

    }

    //关闭通道

    fin.close();

    fout.close();

}

5.2.2. 利用内存映射文件完成(直接缓冲区)

直接缓冲区


public void test2() throws Exception {

    //获取通道

    FileChannel fin = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);

    FileChannel fout = FileChannel.open(Paths.get("2.txt"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);



    //内存映射

    MappedByteBuffer inBuf = fin.map(MapMode.READ_ONLY,0,fin.size());

    MappedByteBuffer oBuf = fout.map(MapMode.READ_WRITE,0,fin.size());



    //直接对缓冲区进行数据读写操作

    Byte[] dst = new Byte[inBuf.limit()];



    inBuf.get(dst);

    oBuf.put(dst);



    fin.close();

    fout.close();

}

5.2.3. 通道之间的数据传输

直接缓冲区


public void test3() throws Exception {

    FileChannel fin = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);

    FileChannel fout = FileChannel.open(Paths.get("2.txt"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);

    //直接利用通道传输

    //fin.transformTo(0,fin.size(),fout);

    fout.transformFrom(fin,0,fin.size());

    fin.close();

    fout.close();

}

5.2.4. 分散(Scatter)与聚集(Gather)

  • 分散读取(Scattering Read):将通道中的数据按顺序分散到多个缓冲区

  • 聚集写入(Gathering Write):将各个缓冲区的数据按顺序聚集到通道中


public void test4() throws Exception{

    //获得随机访问文件

    RandomAccessFile file = new RandomAccessFile("1.txt","rw");

    //获得通道

    FileChannel fileChannel = file.getChannel();

    //获得指定大小的缓冲区

    ByteBuff buf1 = ByteBuff.allocate(100);

    ByteBuff buf2 = ByteBuffer.allacate(1024);

    ByteBuffer[] bufs = {buf1,buf2};

    //分散读到缓冲区

    fileChannel.read(bufs);

    //将每个缓冲区切换到读模式

    for(ByteBuffer buf : bufs){

        buf.flip();

    }

    System.out.println(new String(bufs[0],0,bufs[0].limit()));

    System.out.println("============================");

    System.out.println(new String(bufs[1],0,bufs[1].limit()));

    //聚集写入

    RandomAccessFile file2 = new RandomAccessFile("2.txt","rw");

    FileChannel fileChannel2 = file2.getChannel();

    fileChannel2.write(bufs);



    fileChannel.close();

    fileChannel2.close();

}