NIO概述及Buffer的基本使用

NIO概述

java.nio全称Java non-blocking IO或Java New IO,是从jdk1.4 开始引入的一套新的IO api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供同步非阻塞式的高伸缩性网络。

NIO核心组成部分:
  • Buffer:缓冲区
  • Channel:通道
  • Selector:选择器(轮询器)
NIO和IO的区别
IONIOAIO
面向流(Stream Oriented)面向缓冲区(Buffer Oriented)AIO是发出IO请求后,由操作系统自己去获取IO权限并进行IO操作
阻塞IO(Blocking IO)非阻塞(Non Blocking IO)AIO只是帮助你从内核中将数据复制到用户空间中,并调用你传入的回调方法
选择器(Selectors)

AIO的做法是,每个水壶上装一个开关,当水开了以后会提醒对应的线程去处理。

NIO的做法是,叫一个线程不停的循环观察每一个水壶,根据每个水壶当前的状态去处理。

BIO的做法是,叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。

Buffer的使用

Java NIO中的Buffer用于和NIO通道进行交互,而数据是从通道读入缓冲区,从缓冲区写入到通道中的。
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。

可以将这个Buffer看做是一个运载车,存储数据,在内存和外部设备之间的通道上进行运输,所以必然有装载和卸货的过程,这里可以进行读写模式切换。

Java NIO里关键的Buffer实现:
  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

一、相关方法

1.Buffer缓冲区的分配

要想获得一个Buffer对象首先要进行分配。 每一个Buffer类都有一个allocate方法。下面是一个分配1024字节capacity的ByteBuffer的例子。

//创建间接缓冲区,大小为1024个字节,是HeapByteBuffer子类
ByteBuffer buf = ByteBuffer.allocate(1024);

//直接缓冲区,是MappedByteBuffer的子类
ByteBuffer buf = ByteBuffer.allocateDirect(1024);

直接缓冲区和见解缓冲区的区别
**间接缓冲区:**在堆中开辟,易于管理,垃圾回收器可以回收,空间有限,读写文件速度较慢。
**直接缓冲区:**不在堆中,物理内存中开辟空间,空间比较大,读写文件速度快,缺点:不受垃圾回收器控制,创建和销毁耗性能。

2.向Buffer写入数据的方法

写数据到Buffer有两种方式:

  • 从Channel写到Buffer。后面章节中会做出案例说明。
  • 通过Buffer的put()方法写到Buffer里。
//将数据写到Buffer中,除了以下字节方法,还有putInt,putDouble等数据方法
put(byte b);
put(byte[] src);
put(byte[] src, int offset, int length);
put(ByteBuffer src);
put(int index, byte b); 
3.读写模式切换
//读写模式切换,一般是写转读
Buffer flip();
4.从Buffer中读取数据

从Buffer中读取数据有两种方式:

  1. 从Buffer读取数据到Channel(后面章节会介绍)
  2. 使用get()方法从Buffer中读取数据
//获取数据,除了以下字节方法,还有getInt、getDouble等数据方法
byte get();
ByteBuffer get(byte[] dst);
ByteBuffer get(byte[] dst, int offset, int length);
byte get(int index);

//Buffer转为读取模式后,直接将其转为字节数组
byte[] array();
5.重读
//重绕此缓冲区
Buffer rewind();
6.清空
//清除缓冲区,其实并不是真正意义上的清楚,原数据还在,只是覆盖写入
Buffer clear();

//压缩此缓冲区,用于未读完的情况,将未读数据前置(原已读数据被覆盖),从空位置续写,而不是覆盖写
ByteBuffer compact();
7.标记
//添加标记
buffer.mark(); 

//恢复到标记位置
buffer.reset();
8.获取相关的Buffer缓冲区属性方法
//返回此缓冲区的容量(创建的时候指定的长度,一般不可变)
int capacity();

//返回缓冲区的数据长度
int limit();

//返回此缓冲区指针所在的位置,默认从0位置开始
int position()

此外,还有mark标记属性,这四个属性的活动范围(值的大小)关系为:
0 <= mark <= position <= limit <= capacity

二、Buffer的实现原理

该方法的读写原理主要是操作 position 和 limit 这两个属性完成的,本质上操作的是数组。

测试代码如下:

//测试代码
public class BufferDemo2 {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        System.out.println("--------写入数据之前-------");
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());

        System.out.println("--------写入数据之后-------");
        buffer.put("hello".getBytes());
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());

        System.out.println("-------转换为读取模式后-----");
        buffer.flip();
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());

        //读取
        System.out.println("-------读取之后-----");
        byte[] data = new byte[buffer.limit()];
        buffer.get(data);
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());

        System.out.println("---rewind()重复读取--");
        buffer.rewind();
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());

        System.out.println("-----读取三个字节,但是没读完hello-----");
        byte b1 = buffer.get();
        byte b2 = buffer.get();
        byte b3 = buffer.get();//读取三个字节,但是没读完hello
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());

        System.out.println("----compact压缩,后面未读完的前移----");
        buffer.compact();
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());
        buffer.flip();
        System.out.println("flip后限制" + buffer.limit());
        byte b4 = buffer.get();
        byte b5 = buffer.get();
        System.out.println((char) b4);
        System.out.println((char) b5);


        //清空,只是把指针位置修改了,内容还在,可被覆盖
        System.out.println("------清空----------");
        buffer.clear();
        System.out.println("容量" + buffer.capacity());
        System.out.println("限制" + buffer.limit());
        System.out.println("位置" + buffer.position());
    }
}

//打印结果
--------写入数据之前-------
容量1024
限制1024
位置0
--------写入数据之后-------
容量1024
限制1024
位置5
-------转换为读取模式后-----
容量1024
限制5
位置0
-------读取之后-----
容量1024
限制5
位置5
---rewind()重复读取--
容量1024
限制5
位置0
-----读取三个字节,但是没读完hello-----
容量1024
限制5
位置3
----compact压缩,后面未读完的前移----
容量1024
限制1024
位置2
flip后限制2
l
o
------清空----------
容量1024
限制1024
位置0

写入数据前
在这里插入图片描述
写入数据后
在这里插入图片描述
转为读取模式后
在这里插入图片描述
读取之后
在这里插入图片描述
rewind()重读
在这里插入图片描述
读取三个字节,但是没读完
在这里插入图片描述
compact压缩,后面未读完的前移,写入的时候是追加写

compact()方法执行后,Buffer处于写模式(追加写)
在这里插入图片描述
使用 flip() 方法后
在这里插入图片描述
清空clear()
在这里插入图片描述
清空后所有属性归位,处于写模式,但是里面的数据还在,也就是覆盖写。

mark标记和reset返回标记

public class BufferMarkDemo3 {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
        buffer.put("hello".getBytes());
        buffer.flip();
        byte[] data = new byte[2];
        buffer.get(data);
        System.out.println("第一次读取");
        System.out.println(new String(data));
        buffer.mark();
        buffer.get(data);
        System.out.println("第二次读取");
        System.out.println(new String(data));
        buffer.reset();//回到上一个标记位置
        buffer.get(data);
        System.out.println("第三次读取");
        System.out.println(new String(data));
    }
}

//打印结果:
第一次读取
he
第二次读取
ll
第三次读取
ll

在这里插入图片描述

三、Buffer的使用基本步骤

  1. 创建缓冲区,容量自定义
  2. 向缓冲区中添加内容
  3. 切换为读模式
  4. 批量读取,limit方法调取容量
  5. 清空
public class BufferDemo {
    public static void main(String[] args) {
        //1.创建缓冲区,容量1024
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        //2.写入数据
        buffer.put("我爱魔兽世界".getBytes());

        //3.切换模式,写入模式转为读取模式
        buffer.flip();

        //4.读取,分为单字节读取和批量读取
        /*4.1单字节读取
        byte b = buffer.get();
        System.out.println((char) b);*/
        /*4.2字节数组读取
        byte[] data = new byte[buffer.limit()];
        buffer.get(data);   //此方法将此缓冲区的字节传输到给定的目标数组中
        System.out.println(new String(data));
		*/
		//4.3缓冲区直接转字节数组读取
		System.out.println(new String(buffer.array(), 0, buffer.limit()));
		
        //5.清空
        buffer.clear();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值