NIO总结(一) NIO之Buffer

引言:为什么会出现NIO?

我们知道JDK很早就提供对IO操作-----BIO(同步阻塞IO),那为什么还要提供NIO呢?翻阅了一些资料以后,我觉得NIO出现的原因主要是为了解决BIO中不必要的线程上下文切换。

下面我们以聊天室来解释:
我们知道BIO是阻塞式IO,当服务端接受到一个Socket连接的时候,我们需要开一个线程来处理这个Socket连接中的网络请求(单线程是无法同时处理多个Socket请求的)。如果说这个聊天室的在线活跃人数非常多的话(比如说,有100万个用户),我们难道要开100万个线程(能开的了吗?),还有聊天室中的每个人不可能一直聊天吧?如果有很多用户只是进入了聊天室,但是什么都不做,那我们给他开一个线程,还要经常切换到该线程,岂不是很浪费资源(线程上下文切换也是消耗资源的!),所以使用BIO对服务器资源要求比较高。为了解决了这个问题,JDK的大佬们在1.4版本就推出了NIO。。。。。

开始瞎掰:

Java NIO 由以下几个核心部分组成:

1 、Buffer

2、Channel

3、Selector

传统的IO操作面向数据流,意味着每次从流中读一个或多个字节,直至完成,数据没有被缓存在任何地方。

NIO操作面向缓冲区,数据从Channel读取到Buffer缓冲区,随后在Buffer中处理数据。

Buffer

根据类型的不同,提供了相应类型的缓冲区
在这里插入图片描述
不过一般常用的是ByteBuffer,适合所有。

一块缓存区,内部使用字节数组存储数据,并维护几个特殊变量,实现数据的反复利用。

缓冲区的四大核心属性

在这里插入图片描述在这里插入图片描述
mark():把当前的position赋值给mark

public final Buffer mark() {
    mark = position;
    return this;
}
``

reset():把mark值还原给position

public final Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}

clear():一旦读完Buffer中的数据,需要让Buffer准备好再次被写入,clear会恢复状态值,但不会擦除数据。

public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

flip():Buffer有两种模式,写模式和读模式,flip后Buffer从写模式变成读模式。

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

rewind():重置position为0,从头读写数据。

public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

缓存区存取数据的两个核心方法

  1. put():存入数据到缓冲区
  2. get():获取缓冲区中的数据

NIO基本操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

直接缓冲区与间接缓冲区

在这里插入图片描述

非直接缓冲区

在这里插入图片描述调用allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中

ByteBuffer的实现类"HeapByteBuffer"的allocate代码:

public static ByteBuffer allocate(int capacity) {
    if (capacity < 0)
        throw new IllegalArgumentException();
    return new HeapByteBuffer(capacity, capacity);
}
HeapByteBuffer(int cap, int lim) {  
    super(-1, 0, lim, cap, new byte[cap], 0);
}

HeapByteBuffer通过初始化字节数组hd,在虚拟机堆上申请内存空间。

直接缓冲区

在这里插入图片描述
通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中

ByteBuffer的实现类"DirectByteBuffer"的allocateDirect代码:

public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}
DirectByteBuffer(int cap) {
    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;
}

DirectByteBuffer通过unsafe.allocateMemory申请堆外内存,并在ByteBuffer的address变量中维护指向该内存的地址。

unsafe.setMemory(base, size, (byte) 0)方法把新申请的内存数据清零。

发布了45 篇原创文章 · 获赞 3 · 访问量 2322

猜你喜欢

转载自blog.csdn.net/weixin_44046437/article/details/99628455