NIO

作者: AC编程 | 来源:发表于2020-08-11 12:16 被阅读0次

一、IO和NIO区别

  • IO是面向流的,阻塞的,单向的;
  • NIO是面向缓冲的,非阻塞的,双向的;

二、缓冲区

2.1 基本概念

缓冲区(Buffter):在Java NIO中负责数据的存储,缓冲区就是数组,用于存储不同数据类型的数据。

JDK中8大基本类型,除了boolean没有提供对应的Buffter类外,其他7大基本类型都提供了对应的Buffter类,如:ByteBuffer、CharBuffer等等。

上述缓冲区的管理方式基本一致,都是通过allocate()获取缓冲区。

2.2 缓冲区中的四个核心属性
private int mark = -1    // 标记
private int position= 0  // 位置,表示缓冲区正在操作数据的位置
private int limit   // 界限,表示缓冲区可以操作数据的大小
private int capacity  // 容量,表示缓冲区中最大存储数据的容量
2.3缓冲区中的两个核心方法
put()  // 存储数据到缓冲区
get()  // 获取缓冲区中的数据
2.4 代码演示
import java.nio.ByteBuffer;

public class BufferTest {

    public static void main(String[] args) {
        test();
    }

    public static void test(){
        // 1、分配一个指定大小的缓冲区
        System.out.println("---------------allocate----------------");
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        System.out.println(byteBuffer.position()); // 0
        System.out.println(byteBuffer.limit()); // 1024
        System.out.println(byteBuffer.capacity()); // 1024

        // 2、利用put()存入数据到缓冲区
        System.out.println("---------------put----------------");
        String str = "abcde";
        byteBuffer.put(str.getBytes());
        System.out.println(byteBuffer.position()); // 5
        System.out.println(byteBuffer.limit()); // 1024
        System.out.println(byteBuffer.capacity()); // 1024

        // 3、切换到读模式 flip()
        System.out.println("---------------flip----------------");
        byteBuffer.flip();
        System.out.println(byteBuffer.position()); // 0
        System.out.println(byteBuffer.limit()); // 5
        System.out.println(byteBuffer.capacity()); // 1024

        // 4、利用get读取缓冲区中的数据
        System.out.println("---------------read----------------");
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        System.out.println(new String(bytes)); // abcde
        System.out.println(byteBuffer.position()); // 5
        System.out.println(byteBuffer.limit()); // 5
        System.out.println(byteBuffer.capacity()); // 1024
    }
}
2.4 直接缓冲区和非直接缓冲区
  • 非直接缓冲区:通过allocate()方法分配的缓冲区,建立在JVM内存中
  • 直接缓冲区:通过allocateDirect()方法分配的直接缓冲区,建立在物理内存,这样可以提高效率

通过查看allocate()方法源码,我们可以看到new HeapByteBuffer(capacity, capacity),因此印证了allocate是在JVM堆内存中开辟一块空间。

  /**
     * Allocates a new byte buffer.
     *
     * <p> The new buffer's position will be zero, its limit will be its
     * capacity, its mark will be undefined, and each of its elements will be
     * initialized to zero.  It will have a {@link #array backing array},
     * and its {@link #arrayOffset array offset} will be zero.
     *
     * @param  capacity
     *         The new buffer's capacity, in bytes
     *
     * @return  The new byte buffer
     *
     * @throws  IllegalArgumentException
     *          If the <tt>capacity</tt> is a negative integer
     */
    public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

通过查看allocateDirect()方法源码,我们可以看到VM.isDirectMemoryPageAligned()

  public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

三、通道

3.1 概念

通道(Channel):通道就是IO源和目标打开的连接,通道本身不存储数据,需要配合缓冲区使用。

3.2 非直接缓冲区 & 直接缓冲区 性能对比

在本地磁盘有一个600M文件,我们用非直接缓冲区 和 直接缓冲区两种方式对其拷贝,看那种方式执行效率更高。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 通道:本身不存储数据,需要配合缓冲区使用
 *
 * 通道的主要实现类:
 * java.nio.channels.Channer接口
 *      /- FileChannel (本地)
 *      /- SocketChannel (网络TCP)
 *      /- ServerSocketChannel (网络TCP)
 *      /- DatagramChannel (网络UDP)
 *
 * 如何获取一个通道
 * 本地IO:
 *     FileInputStream/FileOutputStream
 *     RandemAccessFile
 *  网络IO:
 *     Socket
 *     ServerSocket
 *     DatagramSocket
 *
 */
public class ChannelTest {

    public static void main(String[] args) throws Exception {
        testAllocate();
        testDirect();
    }

    /**
     * 通道+非直接缓冲区 实现文件的复制
     * @throws Exception
     */
    public static void testAllocate() throws Exception{

        long start = System.currentTimeMillis();

        FileInputStream fileInputStream = new FileInputStream("D:/devsoft/test/ideaIU.exe");
        FileOutputStream fileOutputStream = new FileOutputStream("D:/devsoft/test/ideaIU_01.exe");

        //获取通道
        FileChannel fileInputStreamChannel = fileInputStream.getChannel();
        FileChannel fileOutputStreamChannel = fileOutputStream.getChannel();

        //分配缓冲区大小(非直接缓冲区)
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        // 将通道中的数据存入到缓冲区
        while(fileInputStreamChannel.read(byteBuffer)!=-1){
            // 切换到读取模式
            byteBuffer.flip();
            fileOutputStreamChannel.write(byteBuffer);
            byteBuffer.clear();
        }

        // 关闭资源操作
        fileInputStreamChannel.close();
        fileOutputStreamChannel.close();
        fileInputStream.close();
        fileOutputStream.close();

        long end = System.currentTimeMillis();

        System.out.println("通道+非直接缓冲区,用时:"+(start - end)+"ms");
    }

    /**
     * 通道+直接缓冲区 实现文件的复制
     * @throws Exception
     */
    public static void testDirect() throws Exception{

        long start = System.currentTimeMillis();

       //1、打开通道
        FileChannel readFileChannel = FileChannel.open(Paths.get("D:/devsoft/test/ideaIU.exe"), StandardOpenOption.READ);
        FileChannel writeFileChannel = FileChannel.open(Paths.get("D:/devsoft/test/ideaIU_02.exe"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);

        // 2、获取内存映射文件(直接缓冲区)
        MappedByteBuffer readMapperBuffer = readFileChannel.map(FileChannel.MapMode.READ_ONLY, 0, readFileChannel.size());
        MappedByteBuffer writeMapperBuffer = writeFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, readFileChannel.size());

        // 3、直接对缓冲区进行数据的读写操作
        byte[] des = new byte[readMapperBuffer.limit()];
        readMapperBuffer.get(des);
        writeMapperBuffer.put(des);

        // 4、关闭资源操作
        readFileChannel.close();
        writeFileChannel.close();

        long end = System.currentTimeMillis();

        System.out.println("通道+直接缓冲区,用时:"+(start - end)+"ms");
    }
}

控制台打印结果

通道+非直接缓冲区,用时:-6197ms
通道+直接缓冲区,用时:-1761ms

从执行结果来看,通道+非直接缓冲区,耗时6.1秒;通道+直接缓冲区,耗时1.7秒,很明显,直接缓冲区要比非直接缓冲区读取文件性能更高。

最后给大家送波福利

阿里云折扣快速入口

阿里云折扣快速入口

相关文章

网友评论

      本文标题:NIO

      本文链接:https://www.haomeiwen.com/subject/aigjdktx.html