NIO模型(一)

NIO:同步非阻塞

NIO模型是基于IO复用来实现的,在Java 1.4中被纳入JDK中。在NIO中有几个核心的对象:缓冲区(Buffer)、通道(Channel)、选择器(Selector)

Buffer

在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer。对于Java中的基本数据类型,基本都有一个具体的Buffer类型与之相对应。更直接的说,我觉得Buffer就是一个数组

Buffer中有三个重要的参数:位置(Position)、容量(Capacity)、上限(Limit)

参数 写模式 读模式
位置(Position) 当前缓冲区的位置,将从Position的下一个位置写数据 当前缓冲区读取的位置,将从此位置之后,读取数据
容量(Capacity) 缓冲区的总容量上限 缓冲区的总容量上限
上限(Limit) 缓冲区的实际上限,它总是小于等于容量。通常情况下,和容量相等 代表可读取的总容量,和上次写入的数据量相等

Buffer的常见方法如下:

flip():写模式转换为读模式

rewind():将position重置为0,一般用于重复读

clear()

compact():将未读取的数据拷贝到buffer的头部

mark():reset():mark可以标记一个位置,reset可以重置到该位置

Buffer基础操作代码演示如下:

public class Test0226 {
    public static void main(String[] args) {
        //分配8个长度的int数组
        IntBuffer buffer=IntBuffer.allocate(8);
        //Capacity 数组的容量 长度
        for(int i=0;i<buffer.capacity();++i){
            int j=(i+1);
            //将此数值写入到缓冲区的当前位置,当前位置递增
            buffer.put(j);
        }
        //重设此缓冲区,将限制设置为当前位置,然后将当前位置设置为0
        buffer.flip();
        //查看在当前位置和限制位置之间是否还有元素
        while (buffer.hasRemaining()){
            //读取此缓冲区当前位置的整数,然后当前位置递增
            int j=buffer.get();
            System.out.println(j+"  ");
        }
    }
}

缓冲区存取数据的流程:

存数据时Position++,当停止数据读取的时候,调用flip(),此时limit=position,position=0

读数据时Position++,一直读取到limit,clear()清空Buffer,准备再次被写入(position变成0,limit变成capacity)

Channel

通道是一个对象,通过它可以读取和写入数据,所有的数据都是通过Buffer对象来处理,不是直接将字节写入通道中,而是将数据写入包含一个或者多个字节的缓冲区中;同样的,不会直接从通道中读取字节,而是将数据从通道中读入缓冲区,再从缓冲区获取字节。

使用NIO读取数据代码演示如下:

public class Test0226{
    public static void main(String[] args) throws IOException {
        FileInputStream inputStream=new FileInputStream("c:\\test.txt");
        //获取通道
        FileChannel channel=inputStream.getChannel();
        //创建缓冲区
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        //读取数据到缓冲区
        channel.read(buffer);
        buffer.flip();
        while (buffer.remaining()>0){
            byte bytes=buffer.get();
            System.out.println((char)bytes);
        }
        inputStream.close();
    }
}

Selector

1、选择器Selector

Selector选择器类管理着一个被注册的通道集合的信息和他们的就绪状态。通道和选择器是一起被注册的,并且使用选择器来更新通道的就绪状态。

2、可选择通道SelectorChannel

SelectableChannel这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。SelectableChannel可以被注册到Selector对象上,同时可以指定对那个选择器而言,哪种操作是感兴趣的。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。

3、选择键SelectionKey

选择键封装了特定的通道与特定的选择器的注册关系。选择键对象被SelectableChannel.register()返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),指示了该注册关系所关心的通道操作,以及通道已经准备好的操作。

创建Selector

Selector对象是通过调用静态工厂方法open()来实例化的,如下:

Selector Selector = Selector.open();

将Channel注册到Selector
要实现Selector管理Channel,需要将channel注册到相应的Selector上,如下:

channel.configureBlocking(false);
SelectionKey key= channel.register(selector,SelectionKey,OP_READ);

NIO服务端通信序列图:

步骤:(server)
1、先创建ServerSocketChannel实例、并且绑定端口
2、创建selector实例
3、将ServerSocketChannel实例注册到选择器上,并监听accept事件
4、有事件发生(accept),就获取客户端的socketChannel的连接,将连接实例注册到选择器上
5、(IO操作)通过selector监听读事件,就进行相应的读事件,写事件类似,也需要监听--》Buffer
6、关闭ServerSocketChannel实例

NIO客户端通信序列图:

步骤:(Client)

1、创建SocketChannel实例
2、创建selector选择器
3、连接服务器
4、将SocketChannel注册到selector选择器
5、直接发送数据,通过selector选择器监听读事件
6、关闭SocketChannel实例

猜你喜欢

转载自blog.csdn.net/qq_40303781/article/details/87935178
今日推荐