您的位置 首页 java

1. Java NIO概述

1. 简述

Java NIO(new io non blocking IO 是从java1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有着同样的作用和目的,但是使用的方式完全不同, NIO支持面向缓冲区、基于通道的IO操作 。NIO将以更加高效的方式进行文件的读写操作。

2. Java NIO 与 IO的主要区别

2.1 图解(面向流和面向缓冲区)

3. Java NIO 的 通道(channel)与缓冲区(buffer)

一句话总结:Channel负责传输,Buffer负责存储

3.1 缓冲区(buffer)

3.1.1 直接缓冲区 与 非直接缓冲区

  • 非直接缓冲区 :通过 allocate()方法分配缓冲区,将缓冲区建立在 JVM 内存中
  • 直接缓冲区 :通过 allocateDirect()方法分配直接缓冲区,将缓冲区建立再操作系统的物理内存中。在某种情况下可以提高效率

 package com.wj.nio;

import  Java .nio. byte Buffer;

/**
 * 一. 缓冲区:再Java NIO中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
 *
 * 根据数据类型不同(boolean除外),提供了对应类型的缓冲区
 * ByteBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * CharBuffer
 * DoubleBuffer
 * FloatBuffer
 *
 * 上述缓冲区的管理方式都基本一致,通过allocate()获取缓冲区
 *
 * 二. 缓冲区存储数据的两个核心方法
 * put() :存入数据到缓冲区
 * get() :获取缓冲中的数据
 *
 * 三:缓冲区中的四个核心属性:
 * capacity:容量,表示缓冲区中最大存储数据的容量。一旦声明,不能改变
 * limit:界限,表示缓冲区中可以操作数据的大小。(limit后面的数据是不能进行读写的)
 *  position :位置,表示缓冲区中正在操作数据的位置
 *  mark :标识,表示记录当前position的位置。可通过reset()恢复到mark的位置
 *
 * Invariants: 0 <= mark <= position <= limit <= capacity
 *
 * 四:直接缓冲区 与 非直接缓冲区
 * 非直接缓冲区:通过 allocate()方法分配缓冲区,将缓冲区建立在 JVM 内存中
 * 直接缓冲区:通过 allocateDirect()方法分配直接缓冲区,将缓冲区建立再操作系统的物理内存中。在某种情况下可以提高效率
 */public class TestBuffer {
    public static void main(String[] args) {
//        test1();
//        test2();
        test3();
    }

    public static void test3(){
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); // 直接缓冲区

        // 判断缓冲区是否时直接的
        System.out.println("直接缓冲区:" + byteBuffer.isDirect());

    }

    public static void test2(){
        String str = "abcde";

        ByteBuffer bf = ByteBuffer.allocate(1024); // 非直接缓冲区
        bf.put(str.getBytes());

        // 切换到读取模式
        bf.flip();

        // 读取数据
        byte[] b1 = new byte[bf.limit()];
        bf.get(b1,0,2);
        System.out.println(new String(b1));
        System.out.println(bf.position());

        // 进行定位标记
        bf.mark();

        bf.get(b1,2,2);
        System.out.println(new String(b1));
        System.out.println(bf.position());

        // reset() 恢复到mark的位置
        bf.reset();
        System.out.println(bf.position());

    }

    public static void test1(){
        String str = "abcde";

        // 1. 分配一个指定大小的缓冲区
        ByteBuffer allocate = ByteBuffer.allocate(1024); // 非直接缓冲区
        System.out.println("-----------------allocate----------------");
        System.out.println(allocate.position()); // 位置
        System.out.println(allocate.limit()); // 界限
        System.out.println(allocate.capacity()); // 容量

        // 2. 利用put()添加数据
        allocate.put(str.getBytes());
        System.out.println("-----------------put()----------------");
        System.out.println(allocate.position()); // 位置
        System.out.println(allocate.limit()); // 界限
        System.out.println(allocate.capacity()); // 容量

        // 3. 切换成读取数据的模式
        allocate.flip();
        System.out.println("-----------------flip()----------------");
        System.out.println(allocate.position()); // 位置
        System.out.println(allocate.limit()); // 界限
        System.out.println(allocate.capacity()); // 容量

        // 4. 读取缓冲区数据
        byte[] dst = new byte[allocate.limit()];
        allocate.get(dst);
        System.out.println("-----------------get()----------------");
        System.out.println(new String(dst,0,dst.length));
        System.out.println(allocate.position()); // 位置
        System.out.println(allocate.limit()); // 界限
        System.out.println(allocate.capacity()); // 容量

        // 5.  rewind() 重读
        allocate.rewind();
        System.out.println("-----------------rewind()----------------");
        System.out.println(allocate.position()); // 位置
        System.out.println(allocate.limit()); // 界限
        System.out.println(allocate.capacity()); // 容量

        // 5.clear() 清空缓冲区,但是缓冲区中的数据依然存在,只不过数据处于被遗忘状态,就是指针回到原始状态,但是数据没丢
        allocate.clear();
        System.out.println("-----------------clear()----------------");
        System.out.println(allocate.position()); // 位置
        System.out.println(allocate.limit()); // 界限
        System.out.println(allocate.capacity()); // 容量
    }
}
  

3.2 通道(channel)

3.2.1 通道演化图解

  • 由CPU 独立 负责IO接口,当发起大量请求时,CPU占用率就会非常高,导致CPU无法做其他工作。

  • DMA (直接存储器存储) :当有请求IO操作时,DMA向CPU申请权限,那么所有的IO接口将由DMA全权进行处理。 传统IO操作就是这种方式。 但是这种方式也有缺陷:当有非常大的应用程序发起大量的IO读写请求时,DMA会创建大量的DMA总线,造成冲突,最终造成资源浪费。

  • 通道(Channel): 完全独立的处理器,专门用于IO操作。 ,虽然依附于CPU,但是和CPU没有任何关系,不会牵连到CPU。

3.2.2 Java为Channel接口提供的最主要实现类如下:

3.2.3 分散(Scatter)和聚集(Gather)

 package com.wj.nio;


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.Standard open Option;
import java.util.Map;

/**
 * 一:通道(Channel):用于源节点与目标节点的链接,再Java NIO中负责缓冲区中数据的传输。Channel本省不存储数据,因此需要配合缓冲区进行传输操作。
 *
 * 二:通道的一些主要实现类:
 *      java.nio.channels.Channel
 *          |--FileChannel:完成本地文件数据传输
 *          |--SocketChannel:通过TCP读取网络中的数据
 *          |--ServerSocketChannel:监听新进来的TCP连接,对每一个连接创建SocketChannel
 *          |--DatagramChannel:通过UDP读取网络中的数据
 *
 *  三:获取通道
 *  1. Java针对支持通道的类提供了 getChannel() 方法,
 *        本地IO:
 *          FileInputStream/FileOutputStream
 *          RandomAccessFile:随即存储文件流
 *        网络IO:
 *          Socket
 *          ServerSocket
 *          DatagramSocket
 *
 *  2. jdk1.7中的NIO.2对NIO进行了更新。正对各个通道提供了静态放法 open()
 *
 *  3. jdk1.7中的NIO.2的File工具类的newByteChannel()
 *
 *  四、通道之间的数据传输
 *      transfreFrom()
 *      transfreTo()
 *
 *  五、分散(Scatter)于聚集(Gather)
 *      分散读取(Scatter Reads):将通道中的数据分散到多个缓冲区
 *      聚集写入(Gather Writes):将多个缓冲区的数据聚集到通道中
 *
 *  六:字符集:Charset
 *       编码 :字符串转换为字节数组
 *      解码:字节数组转换为字符串
 */public class TestChannel {
    public static void main(String[] args) throws Exception {
//        test01(); // 基于流的通道
//        test02(); // open()方式的通道
//        test03(); // 通道之间的数据传输
//        test04(); // 分散(scatter)于聚集(Gather)
//        test05(); // 字符集的编码与解码
        test06(); // 字符集的编码与解码

    }

    /**
     * 字符集的编码与解码
     */    private static void test06() throws Exception {
        // 创建字符集
        Charset gbk = Charset.forName("GBK");
        // 获取编码器
        CharsetEncoder charsetEncoder = gbk.newEncoder();
        // 获取解码器
        CharsetDecoder charsetDecoder = gbk.newDecoder();

        //
        CharBuffer allocate = CharBuffer.allocate(1024);
        allocate.put("测试编码");
        allocate.flip();

        // 编码
        ByteBuffer encode = charsetEncoder.encode(allocate);
        for (int i=0; i<8;i++) {
            System.out.println(encode.get());
        }
        encode.flip();

        // 解码
        CharBuffer decode = charsetDecoder.decode(encode);

        System.out.println(decode.toString());
    }

    /**
     * 查询支持的编码格式
     */    private static void test05() {
        Map<String, Charset> stringCharsetSortedMap = Charset.availableCharsets();
        // 查询支持的编码格式
        for(Map.Entry<String, Charset> en : stringCharsetSortedMap.entrySet()){
            System.out.println(en.getKey() + "=" + en.getValue());
        }

    }

    /**
     * 分散(scatter)于聚集(Gather)
     */    private static void test04() throws Exception {
        RandomAccessFile raf = new RandomAccessFile("srcurl","rw");

        // 1. 获取通道
        FileChannel channel = raf.getChannel();

        // 2. 创建缓冲区
        ByteBuffer bf1 = ByteBuffer.allocate(100);
        ByteBuffer bf2 = ByteBuffer.allocate(1024);

        // 3. 分散读取
        ByteBuffer[] bfs = {bf1,bf2};
        channel.read(bfs);

        for (ByteBuffer byteBuffer : bfs) {
            byteBuffer.flip();
        }

        // 测试
        System.out.println(new String(bfs[0].array(), 0, bfs[0].limit()));
        System.out.println("-------------------");
        System.out.println(new String(bfs[1].array(), 0, bfs[1].limit()));

        // 聚集写入操作
        RandomAccessFile raf2 = new RandomAccessFile("dsturl","rw");
        FileChannel channel2 = raf2.getChannel();
        channel2.write(bfs);

        //
        raf. close ();
        channel.close();
        channel2.close();
    }


    /**
     * 通道之间的数据传输
     */    private static void test03() throws Exception{
        FileChannel inChannel = FileChannel.open(Paths.get("srcurl"), StandardOpenOption.READ); // 都属性
        FileChannel outChannel = FileChannel.open(Paths.get("dsturl"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW); // 写属性,不存在就创建,存在就报错

        inChannel.transferTo(0,inChannel.size(),outChannel); // 也是直接缓冲区
//        outChannel.transferFrom(inChannel,0,inChannel.size()); // 和上面的一样

        inChannel.close();
        outChannel.close();
    }

    /**
     * 使用 open() 创建通道 直接使用通道效率高于基于流的通道
     */    private static void test02() throws Exception{
        FileChannel inChannel = FileChannel.open(Paths.get("srcurl"), StandardOpenOption.READ); // 都属性
        FileChannel outChannel = FileChannel.open(Paths.get("dsturl"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW); // 写属性,不存在就创建,存在就报错

        // 创建直接缓存(FileChannel方式)
        MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        // 直接对缓冲区进行数据的读写操作
        byte[] bst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(bst);
        outMappedBuf.put(bst);

        inChannel.close();
        outChannel.close();
    }


    /**
     * 利用通道完成文件的复制
     * @throws Exception
     */    public static void test01() throws Exception{
        FileInputStream fis = new FileInputStream("srcurl");
        FileOutputStream fos = new FileOutputStream("dsturl");

        // 创建非直接缓存区
        ByteBuffer bf = ByteBuffer.allocate(1024);

        // 创建通道
        FileChannel inchannel = fis.getChannel();
        FileChannel outchannel = fos.getChannel();

        // 数据传输
        while(inchannel.read(bf) != -1){
            // 切换为读取模式
            bf.flip();
            // 将数据输出
            outchannel.write(bf);
            // 清空缓存
            bf.clear();
        }

        // 关闭流
        if(fis != null){
            fis.close();
        }
        if(fos != null){
            fos.close();
        }
        if(inchannel != null){
            inchannel.close();
        }
        if(outchannel != null){
            outchannel.close();
        }

    }
}  

文章来源:智云一二三科技

文章标题:1. Java NIO概述

文章地址:https://www.zhihuclub.com/182051.shtml

关于作者: 智云科技

热门文章

发表回复

您的电子邮箱地址不会被公开。

网站地图