Fork me on GitHub

java拓展----NIO

一、介绍java中的IO

  java中的IO分许多种 

  具体介绍请往这走http://blog.51cto.com/stevex/1284437

  java IO 中的NIO的英译名为(new IO ) 他是的出现时在JDK1.4版本才有的。

  NIO 的它实则是一种非阻塞的IO,它是一种基于缓存区的IO读写。

  以前的BIO是一种基于流的读写。每次读写都消耗一个字节数据。而NIO则是一种面向块的读写。每次读写都消耗一个数据块。在操作的速度上要比BIO快许多。

二、使用NIO

  首先介绍一下NIO的必须要知道的几个词汇

  1.通道  2.缓冲区

  通道 和 缓冲区 是 NIO 中的核心对象,几乎在每一个 I/O 操作中都要使用它们。

  通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。

  NIO同时支持多路IO复用。(多个通道连接到同一个缓冲区)

  

  NIO中程序操作的是缓冲区。文件的读取步骤将缓冲区中的数据读取全部读取到。然后将缓冲区中的缓存内容清除。NIO中文件写入操作则是将缓存中的缓存内容写入到文件中去。

·   以下是一个基于BIO的简单文件拷贝的实现。

package com.demo.io.nio;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileChannelTest {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\Users\\Desktop\\test.jsp");
        FileChannel foc = new FileOutputStream("C:\\Users\\Desktop\\test.txt").getChannel();
        FileChannel fic = fis.getChannel();
        //创建一个缓冲区大小为48的ByteBuffer
        ByteBuffer bb = ByteBuffer.allocate(2);
        int br = 0;
        while((br=fic.read(bb))!= -1) {
            //bb.flip() 的调用,首先读取数据到Buffer,然后反转Buffer,接着再从Buffer中读取数据
            bb.flip();
            //告诉当前位置和极限之间是否存在任何元素(此缓冲区中有元素)
            while(bb.hasRemaining()) {
                foc.write(bb);
            }
            System.out.println();
            //缓存满了就会清理缓存
            bb.clear();
        }
        fic.close();
        fis.close();        
    }
}

 

三、简单的文件操作

  操作流程图:

 

代码:

public class NIOFileChannel {
  public static void main(String[] args) throws IOException {
    String str = "hello world";
    //  创建一个输出流
    FileOutputStream fileOutputStream = new FileOutputStream("C:\\file\\file01.txt");
    //  使用输出流获取到对应的FileChannel
    FileChannel fileChannel = fileOutputStream.getChannel();
    //  创建一个缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    //  将要写入的数据放入byteBuffer中
    byteBuffer.put(str.getBytes());
    //  反转ByteBuffer
    byteBuffer.flip();
    //  将缓冲区的数据写入通道中
    fileChannel.write(byteBuffer);
    fileChannel.close();
    fileOutputStream.close();
  }
}
文件写入
public class NIOFileChannelRead {
  public static void main(String[] args) throws IOException {
    //  创建文件输入流
    FileInputStream fileInputStream = new FileInputStream("C:\\file\\file01.txt");
    FileChannel fileChannel = fileInputStream.getChannel();
    //  创建缓冲区
    ByteBuffer byteBuffer = ByteBuffer.allocate(4);
    while (true) {
      //  将数据读取到fileChannel中
      int read = fileChannel.read(byteBuffer);
      if (read == -1) {
        break;
      }
      //  这里如果缓冲区满了需要清空一下才能继续读
      byteBuffer.clear();
      //  输出数据内容
      System.out.println(new String(byteBuffer.array()));
    }
    fileChannel.close();
    fileInputStream.close();
  }
}
文件读取
 1 public class NIOFileChannelCopy {
 2   public static void main(String[] args) throws IOException {
 3     // 创建相关流
 4     FileInputStream fileInputStream = new FileInputStream("C:\\file\\file01.txt");
 5     FileOutputStream fileOutputStream = new FileOutputStream("C:\\file\\file02.txt");
 6     //  获取相关管道
 7     FileChannel sourceCh = fileInputStream.getChannel();
 8     FileChannel destCh = fileOutputStream.getChannel();
 9 
10     //  使用transferForm完成拷贝
11     destCh.transferFrom(sourceCh, 0, sourceCh.size());
12     sourceCh.close();
13     destCh.close();
14   }
15 }
文件拷贝

四、零拷贝

大家都说NIO的效率很高,使用到了零拷贝,至于什么是零拷贝,我们需要先探究一下Java文件读取机制。

零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。它的作用是在数据报从网络设备到用户程序空间传递的过程中,减少数据拷贝次数,减少系统调用,实现 CPU 的零参与,彻底消除 CPU 在这方面的负载。实现零拷贝用到的最主要技术是 DMA 数据传输技术和内存区域映射技术。

简单点讲就是零拷贝就是没有CPU拷贝

BIO拷贝方式(经过4次拷贝,三次态的切换)

1. DMA拷贝:直接内存拷贝,不使用CPU

2.CPU拷贝:讲内存中的数据经过CPU拷贝到用户缓冲区

3.CPU拷贝:讲数据拷贝到Socket buffer中

4.DMA拷贝:将数据拷贝到我们的协议栈

mmap拷贝方式(经过三次拷贝,三次态的切换)

1. DMA 拷贝

2.用户缓冲区与内核缓冲区存在内存映射不会进行拷贝

3.CPU拷贝到socket buffert

4.socket buffer DMA拷贝到协议栈

sendFile(经过两次次拷贝,两次态的切换)

Linux 2.1 提供

1.DMA拷贝

2.内核缓冲区拷贝到sockert buffer

3.DMA拷贝到协议栈

 Linux 2.4提供

1.DMA拷贝

2.内核缓冲区直接DMA拷贝到协议栈

五、后记

个人觉得Java NIO 的API 是非常难用的。这里我推荐大家使用Netty。Netty封装的API相对来说好用很多。

posted @ 2018-12-24 10:57  Banana_Fish  阅读(201)  评论(0编辑  收藏  举报