NIO教程(1)

简介: NIO教程

一,概述

原本的java是基于同步阻塞式的i/o通信(bio) 性能低下,所以出现了nio这种非阻塞式的

二,Java 的I/O演进之路

2.1 i/o模型基本说明

i/o模型:就是用什么样的通道或者说通信模式和架构进行数据的传输和接收,很大程度上决定了程序通信的性能,java支持的3种网络编程的io模型:BIO,NIO,AIO

2.2 I/O模型

BIO

一个连接是一个线程

NIO

同步非阻塞的,客户端发送的连接请求都会注册到多路复用器上,多路复用器查询到连接有i/o请求就会进行处理

AIO

异步非阻塞

2.3 BIO,NIO,AIO 使用场景分析

  1. bio适合连接数目小的且固定的架构
  2. nio适合连接数目多且连接比较短的架构(聊天)
  3. AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充

四 ,NIO

4.1 NIO 基本介绍

  • 面向缓冲区基于通道的
  • 在java.nio包下以及子包下
  • 有三大核心 **Channel(通道),Buffer(缓冲区),Selector(选择器)
  • 如果通道里面没有数据就会去做其他事情不会去等待

4.2 NIO 和 BIO 比较

  • BIO 以流的方式,NIO 以块的方式,效率更高
  • NIO是非阻塞的
  • BIO 是基于字节流和字符流进行操作,而NIO是基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是由缓冲区写入通道或者从通道读入缓冲区,选择器用于监听

4.3 NIO 三大核心原理

三大核心分别为 Channel(通道) , Buffer(缓冲区),Selector(选择器)

Buffer缓冲区

Channel(通道)

通道可以非阻塞读取和写入通道,通道可以支持读取或写入缓冲区,也支持异步地读写,而流是单向的

Selector(选择器)

可以检测多个通道

程序切换到哪个channel是由事件决定的

4.4 缓冲区(Buffer)

一个容器,由java.nio包定义,所有缓冲区都是Buffer抽象类的子类,

Buffer类与其子类

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

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

buffer.wrap()                     数据已知

ByteBuffer.allocate()         构建ByteBuffer对象,参数实际上是指底层的字节数组的容量

缓冲区的基本属性

  • 容量 创建后不能改变
  • 限制 (limit) limit后面不能读写,不能为负,不能大于其容量,写入模式,限制等于buffer的容量,读取模式下,limit等与写入的数据量
  • 位置(position): 下一个读取或写入的数据的索引,索引库的位置不能为负,并且不能大于其限制
  • 标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark() 方法指定Buffer中一个特定的position,之后通过调用reset()方法恢复到这个position
    标记、位置、限制、容量遵守以下不变式: 0<=mark <= position <= limit <= capacity

Buffer常见方法

Buffer clear() 洁空缓冲区并返回对缓冲区的引用(只是把position变为0位置)
Buffer flip(为将缓冲区的界限设置为当前位置,并将当前位置设值为0
使用wrap()   类java.nio.IntBuffer中的方法,可以将int数组包装到缓冲区中。此方法需要一个参数,即将数组包装到缓冲区中,并返回创建的新缓冲区。如果返回的缓冲区被修改,则数组的内容也将被修改,反之亦然。
int capacity(返回 Buffer 的capacity大小
boolean hasRemaining(判断缓区中是否还有元素
int limit()返同Buffer的界限(limit)的位置
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()将位置设为为节、取消设置的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)

缓冲区的数据操作

public class BufferTest {  
    @Test  
    public void test01(){  
        //1.分配一个缓冲区,容量设置10  
        ByteBuffer buffer  = ByteBuffer.allocate(10);  
        System.out.println(buffer.position());  
        System.out.println(buffer.limit());  
        System.out.println(buffer.capacity());  
        System.out.println("---------------------");  
  
        //2.添加数据  
        String  name = "itheima";  
        buffer.put(name.getBytes(StandardCharsets.UTF_8));  
        System.out.println(buffer.position());  
        System.out.println(buffer.limit());  
        System.out.println(buffer.capacity());  
        System.out.println("---------------------");  
        buffer.flip(); //为将缓冲区的界限设置为当前位置,并将当前位置设值为0  
        System.out.println(buffer.position());  
        System.out.println(buffer.limit());  
        System.out.println(buffer.capacity());  
        System.out.println("---------------------");  
  
        //4,. get数据的读取  
        char b = (char)buffer.get();  
        System.out.println(b);  
        System.out.println(buffer.position());  
        System.out.println(buffer.limit());  
        System.out.println(buffer.capacity());  
  
    }  
}
0
10
10
---------------------
7
10
10
---------------------
0
7
10
---------------------
i
1
7
10

直接与非直接缓冲区

什么是直接内存与非直接内存根据官方文档的描述:

byte byffer可以是两种类型,一种是基于直接内存(也就是非堆内存)﹔另一种是非直接内存(也就是堆内存)。对于直接内存来说,JVM将会在IO操作上具有更高的性能,因为它直接作用于本地系统的IO操作。而非直接内存,也就是堆内存中的数据,如果要作IO操作,会先从本进程内存复制到直接内存,再利用本地IO处理。

从数据流的角度,非直接内存是下面这样的作用链:

本地IO-->直接内存-->非直接内存--→>直按内存-->本地IO

而直接内存是:

本地IO-->直接内存-->本地IO

很明显,在做IO处理时,比如网络发送大量数据时,直接内存会具有更高的效率。直接内存使用allocateDirect创建,但是它比申请普通的堆内存需要耗费更高的性能。不过,这部分的数据是在IVM之外的,因此它不会占用应用的内存。所以呢,当你有很大的数据要缓存,并且它的生命周期又很长,那么就比较适合使用直接内存。只是一般来说,如果不是能带来很明显的性能提升,还是推荐直接使用堆内存。字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。

NIO教程(2)https://developer.aliyun.com/article/1530796

相关文章
|
5月前
|
存储 弹性计算 监控
|
5月前
|
Java API
|
5月前
|
Java
Java NIO系列教程三
​ 今天主要给大家介绍的是Buffer的基本使用这个也是NIO里面最总要的概率之一,里面的操作也是有一些复杂的同时也是需要大家必须要重点掌握的知识点,同时也介绍了一下Selector的用法下一篇文章我们将为大家介绍Pipe管道以及FileLock文件锁这也是NIO里面最后的一分部内容了。
99 0
|
安全 Java API
Java NIO系列教程四【完】-管道-文件锁-异步写入
​ 到此位置NIO的所有的内容都结束了,对于NIO来说主要是各种概念需要大家去理解然后有很多的用法和api也需要大家去熟悉所以想把NIO学懂学好其实并不容易一定要多写案例去测试巩固,也预祝大家能把NIO的知识看懂理顺!!!
99 0
|
网络协议 Java
Java NIO系列教程一
今天主要给大家介绍的是NIO的基本的概念以及Channel中常用的FileChannel的基本的用法,算是对Channel有一个简单的介绍。下一篇文章我们将详细的为大家介绍其他的常用Channel。
122 0
Java NIO系列教程一
|
网络协议 Java
Java NIO系列教程二
​ 今天主要是为大家详细的介绍了常见的各种Channel以及他们的用法,本文编写了大量的案例还需要大家认真的去实践以后才能真正的掌握住。介绍完Channel那么下一篇文章我们就可以为大家介绍Buffer和Selector的具体使用了。
87 0
|
Java
Java NIO系列教程(8)-SocketChannel的最佳实践(下)
Java NIO系列教程(8)-SocketChannel的最佳实践
184 0
|
网络协议 Java
Java NIO系列教程(8)-SocketChannel的最佳实践(上)
Java NIO系列教程(8)-SocketChannel的最佳实践
455 0
|
监控 Java
Java NIO系列教程(六) Selector
Java NIO系列教程(六) Selector
135 0