I/O学习:NIO的原理与使用

4 篇文章 0 订阅

NIO学习

标签(空格分隔): I/O


NIO概念

  • NIO支持面向缓冲区的、基于通道的IO操作。NIO能以更加高效的方式进行文件的读写操作。NIO可以理解为非阻塞IO,传统的IO的raed和write只能阻塞执行,线程在读写IO期间不能干其他事情,比如调用socket.raed()时,如果服务器一直没有数据传输过来,线程就一直阻塞,而NIO中可以配置soket为非阻塞式。
  • NIO的三大核心部分:Channel(通道)Buffer(缓冲区)Selector(选择器)
  • NIO非阻塞模式:使一个线程从某通道发送请求或者读取数据,但是他仅能得到目前可用的数据,如果当前没有数据可用时,就说明都不会获取,而不是保持线程阻塞,所以直到数据变得可以读取之前,该线程可以继续做其他的事情,非阻塞写操作也是如此,一个线程写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
  • 即:NIO可以做到用一个线程来处理多个操作。

NIO 与 BIO

  • BIO以流的方式处理数据;NIO以块的方式处理数据,块I/O的效率比流I/O高很多。
  • BIO是阻塞的,NIO则是非阻塞的
  • BIO基于字节流和字符流进行操作,而NIO基于Channel和Buffer进行操作。数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector用于监听多个通道时间(如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
NIOBIO
面向缓冲区(Buffer)面向流(Stream)
非阻塞(Non Blocking IO)阻塞IO(Blocking IO)
选择器(selector)

NIO三大核心

Channel(通道)、Buffer(缓冲区)、Selector(选择器)

1、 Buffer缓冲区

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。相比较直接对数据的操作,Buffer API更加容易操作和管理。

2、 Channel(通道)

Java NIO的通道类似于流,但又有不同:即可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。通道可以非阻塞读取和写入通道,通道可以支持读取或写入缓冲区,一直吃异步地读写。

3、 Selector(选择器)

Selector是一个java NIO组件, 可以能够检查一个或多个NIO通道,并确定哪些通道已经准备好进行读取或写入。这样,一个单独的线程可以管理多个Channel,从而管理多个网络连接,提高效率。

此处输入图片的描述

  • 每个Channel都对应一个Buffer
  • 一个线程对应Selector,一个Selector对应多个Channel
  • 程序切换到那个Channel是由时间决定的
  • Selector会根据不同的时间,在各个通道上切换
  • Buffer就是一个内存块,底层是一个数组
  • 数据的读取写入是用过Buffer完成的,BIO中要么是输入流,要么是输出流,不能双向。但是NIO中Buffer是可以进行读写双向的。
  • javaNIO系统的核心在于:通道和缓冲区。通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及同于容纳数据的缓冲区,然后操作缓冲区,对数据进行处理。简而言之,Channel负责传输,Buffer负责存取数据。

缓冲区(Buffer)

一个用于特定基本数据类型的容器。有java.nio包定义的,所有缓冲区都是Buffer抽象类的子类。java NIO中的Buffer主要用于与NIO通道进行交互,数据时从通道读入缓冲区,从缓冲区写入通道中的。

此处输入图片的描述

Buffer类及其子类

Buffer就像一个数组,可以保存多个相同类型的数据。根据数据类型不同,有一下Buffer常用子类:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

上述Buffer类,他们都采用相似的方法进行管理数据,知识各自管理的数据类型不同而已。都是通过如下方法获取一个Buffer对象:

//创建一个容量为capacity的xxxBuffer对象
static xxxBuffer allocate(int capacity)  

缓冲区的基本属性

Buffer中的重要概念:

  • 容量(capacity):作为一个内存块,Buffer具有一定固定大小,称为“容量”,缓冲区容量不能为负,并且创建后不能更改。
  • 限制(limit):表示缓冲区中可以操作数据的大小(limit后数据不能进行读写)。缓冲区的限制不能为负,并且不能大于其容量。写入模式,limit等于buffer的容量。读取模式下,limit等于写入的数据量
  • 位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制。
  • 标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark()方法指定Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这个position.
  • 标记、位置、限制、容量遵守以下不变式: 0 <= mark <= position <= limit <= capacity

此处输入图片的描述


Buffer常见方法:

Buffer clear()清空缓冲区并返回对缓冲区的引用
Buffer flip()为将缓冲区的界限设置为当前位置,并将当前位置充值为0 0
int capacity()返回 Buffer 的 capacity 大小
boolean hasRemaining()判断缓冲区中是否还有元素
int limit()返回 Buffer 的界限(1imit)的位置
Buffer limit(int n)将设置缓冲区界限为n,并返回一个具有新 limit 的缓冲区对象
Buffer mark()对缓冲区设置标记
int position()返回缓冲区的当前位置 position
Buffer position(int n)将设置缓冲区的当前位置为n ,并返回修改后的 Buffer对象
int remaining()返回 position和 limit 之间的元素个数
Buffer reset()将位置 position转到以前设置的 mark所在的位置
Buffer rewind()将位置设为为0,取消设置的 mark

缓冲区的数据操作:

Buffer所有子类提供了两个用于数据操作的方法: get()put()方法

获取 Buffer中的数据
get():读取单个字节
get(byte[] dst):批量读取多个字节到 dst中
get(int index):读取指定索引位置的字节(不会移动 position)


放到入数据到Buffer中
put(byte b):将给定单个字节写入缓冲区的当前位置put(byte[] src):将 src 中的字节写入缓冲区的当前位置
put(int index,byte b):将指定字节写入缓冲区的索引位置(不会移动 position)

使用Buffer读写数据一般遵循以下四个步骤:

  • 1.写入数据到Buffer
  • 2.调用flip()方法,转换为读取模式
  • 3.从Buffer中读取数据
  • 4.调用buffer.clear()方法或者buffer.compact()方法清除缓冲区

**简单的使用实例:

package com.allwinter;

import org.junit.Test;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

/**
 * com.allwinter
 * 2021/1/29
 * 16:12
 */
public class BufferTest {

    @Test
    public void test2(){
        ByteBuffer buffer = ByteBuffer.allocate(10);
        String inform = "allwiner";
        buffer.put(inform.getBytes());

        buffer.flip();

        //读取数据
        byte[] bytes = new byte[3];
        buffer.get(bytes);
        String res = new String(bytes);
        System.out.println(res);
        BufferInform(buffer);

        //mark()方法:标记此刻这个位置: 2位置
        buffer.mark();

        byte[] bytes1 = new byte[3];
        buffer.get(bytes1);
        String res1 = new String(bytes1);
        System.out.println(res1);
        BufferInform(buffer);

        //reset()方法:回到标记位置
        buffer.reset();
        if (buffer.hasRemaining()){
            //remaining()方法:返回position和limit之间的元素个数
            System.out.println(buffer.remaining());
        }
        BufferInform(buffer);
    }

    @Test
    public void test1(){
        //分配一个缓冲区,容量为10
        ByteBuffer buffer = ByteBuffer.allocate(10);
        BufferInform(buffer);
        //往缓冲区中添加数据
        String inform = "allwinter";
        buffer.put(inform.getBytes());
        BufferInform(buffer);
        //clear()方法:清除缓冲区中的数据
        buffer.clear();
        BufferInform(buffer);
        System.out.println((char)buffer.get());
        /*
            clear方法并没有真正的将数据清除,只是将position重置到了0位置,之后在存入数据才会将之前的数据覆盖掉
         */

    }


    @Test
    public void test0(){
        //分配一个缓冲区,容量为10
        ByteBuffer buffer = ByteBuffer.allocate(10);
        BufferInform(buffer);
        //往缓冲区中添加数据
        String inform = "allwinter";
        buffer.put(inform.getBytes());
        BufferInform(buffer);
        //flip方法: 将缓冲区的界限设置为当前位置,并将当前位置设置为0
        buffer.flip();
        BufferInform(buffer);
        //get获取数据
        char b = (char) buffer.get();
        System.out.println(b);
        BufferInform(buffer);
    }

    public void BufferInform(Buffer buffer){
        System.out.println("position: " + buffer.position());
        System.out.println("limit: " + buffer.limit());
        System.out.println("capacity: " + buffer.capacity());
        System.out.println("-------------------------------");
    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值