在NIO中有三个核心对象需要掌握:缓冲区(Buffer)、选择器(Selector)和通道(Channel)
缓冲区实际上是一个容器对象,更直接地说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的
在读取数据时,它是直接读到缓冲区中的;在写入数据时,它也是写入缓冲区的;任何时候访问NIO中的数据,都是将它放到缓冲区中。
在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer
在缓冲区中,最重要的属性有下面三个,它们一起合作完成对缓冲区内部状态的变化跟踪
position:指定下一个将要被写入或者读取的元素索引,它的值由get()/put()方法自动更新,在新创建一个Buffer对象时,position被初始化为0。
limit:指定还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。
capacity:指定了可以存储在缓冲区中的最大数据容量
缓冲区示意图

缓冲区写入:写入时position右移
缓冲区读取:一是把limit设置为当前的position值。二是把position设置为0。通过position下标从0开始读取,终点为limit。
缓冲区清空:恢复初始状态。
在创建一个缓冲区对象时,会调用静态方法allocate()来指定缓冲区的容量,其实调用allocate()方法相当于创建了一个指定大小的数组,并把它包装为缓冲区对象
在NIO中,除了可以分配或者包装一个缓冲区对象,还可以根据现有的缓冲区对象创建一个子缓冲区,即在现有缓冲区上切出一片作为一个新的缓冲区,现有的缓冲区与创建的子缓冲区在底层数组层面上是数据共享的,子缓冲区相当于现有缓冲区的一个视图窗口。调用slice()方法可以创建一个子缓冲区。
只读缓冲区
只读缓冲区非常简单,可以读取它们,但是不能向它们写入数据。可以通过调用缓冲区的asReadOnlyBuffer()方法,将任何常规缓冲区转换为只读缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化
直接缓冲区是为加快I/O速度,使用一种特殊方式为其分配内存的缓冲区
内存映射是一种读和写文件数据的方法,可以比常规的基于流或者基于通道的I/O快得多
传统的Client/Server模式会基于TPR(Thread per Request),服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的一个问题就是线程数量的剧增,大量的线程会增大服务器的开销。大多数的实现为了避免这个问题,都采用了线程池模型
传统的Client/Server模式

NIO中实现非阻塞I/O的核心对象是Selector,Selector是注册各种I/O事件的地方,而且当那些事件发生时,就是Seleetor告诉我们所发生的事件

使用NIO中非阻塞I/O编写服务器处理程序,大体上可以分为下面三个步骤。
(1)向Selector对象注册感兴趣的事件。
(2)从Selector中获取感兴趣的事件。
(3)根据不同的事件进行相应的处理。
通道是一个对象,通过它可以读取和写入数据,当然所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道,而是将数据写入包含一个或者多个字节的缓冲区
从通道读取到缓冲区的三个步骤
(1)从FileInputStream获取Channel。
(2)创建Buffer。
(3)将数据从Channel读取到Buffer中。