NIO 深入详解和重要体系分析

Java基础

浏览数:121

2019-11-2

AD:资源代下载服务

1、Java NIO 开发的注意点

  • IntBuffer.allocate(10) 分配10个缓冲区,是一个机器生成的,不能new实例,是一个抽象类
  • SecureRandom 提供一个更为健壮的随机数
  • 服务端要将客户端的信息接收到才可以进行分发,采取HashMap将数据存储
  • NIO解决多个客户端的连接的情况,减少CPU的上下文切换的工作量,传统IO的Socket无法解决这个问题,而且多个客户端连接会启用多个线程进行通信

2、java.nio 与 java.io

  • java.nio 在jdk1.4之后出现的包
  • java.nio 拥有3个核心概念: Selector、Channel 、Buffer,在java.nio中,我们是面向块(block)或是缓冲区(Buffer)
  • NIO的事件相当重要,用事件来判断请求与响应
  • NIO是可以存在输入流和输出流,在Buffer读到的数据也可以希尔Buffer当中(flip方法实现)状态翻转.
  • NIO的客户端与传统的Socket不同,只有一个线程进行通信,而传统的Socket编程会启动多个线程进行保证
  • configureBlocking(false); false表示设置非阻塞
  • 网络编程很多场景都是使用死循环进行实现
  • java.io 中最为核心的一个概念是流(Stream)面向流的编程,Stream是信息的载体,I/O要么是输出流,要么是输入流,不可能同时既是输入流又是输出流

3、NIO 3个核心概念关系图如:(重点)

  • [图解关系

  • NIO 3个核心
  • S-代表Selector(Thread)

  • C-代表Channel,

  • B-代表Buffer

  • 异步最核心的原理实现是事件理念

4、Java中有7种原生数据类型

  • Java中有7种原生数据类型都有各自对应的Buffer类型,如:IntBuffer,LongBuffer,CharBuffer等,但没有BooleanBuffer

5、Java NIO的Selector 的 processSelectedKey 中处理了三个事件, 分别是:

  • OP_READ, 可读事件, 即 Channel 中收到了新数据可供上层读取.
  • OP_WRITE, 可写事件, 即上层可以向 Channel 写入数据.
  • OP_CONNECT, 连接建立事件, 即 TCP 连接已经建立, Channel 处于 active 状态

6、Selector(重点)

  • Selector可以监听各种各样的事件(Event)事件当某事情发生时,连接已建立,数据已读取,读取完毕等都是事件
  • Selector通过提供者进行创建,通过系统或自定义提供者进行创建(openSelector)方法调用
  • SelectionKey 注册通道(标识事件)
  • key set (全集subset)
  • selected-key 子集(感兴趣)read与write
  • canceled-key 子集(不感兴趣)取消
  • selected-key是在进行通过添加到Selector,可以移除Set的remove方法,Inteator的remove方法
  • select方法是阻塞的
  • 每当一个Selector注册一个Channel都会SelectionKey
  • Selector一旦建立就可以对事件进行选择与注册,但不是epoll(轮询)
  • register注册到Selector对象上SelectionKey.OP_ACCEPT注册给订单ServerSocketChannel在Selector之上
  • SelectionKey.OP_ACCEPT连接事件,OP_READ读事件
  • isAcceptable月isReadble判断连接和可读数据
  • NIO的客户端的SelectionKey.OP_CONNECT连接,Executors.newSingleThreadExecutor单线程执行
  • Selector检测多个Channel,而一个线程可以被Selector控制,Channel的数据读与写由Buffer来完成

7、Channel(重点)

  • Channel 指的是可以向其写入数据或是从中读取数据的对象,类似java.io的流(Stream)
  • 与Stream不同的是,Channel是双向的,一个流只可能是InputStream或是OutputStream,Channel打开后则可以进行读取,写入或是读写
  • Channel一定是与Buffer一起进行使用实现NIO
  • Channel 没有Buffer是没有用武之地

8、Buffer(重点)

  • Buffer本身就是一块内存,底层实现实际上是一个数组,数据的读写是通过Buffer来实现的(重点),除了数组之外,Buffer还提供了对于数据的结构化访问方式,并且可以追踪到系统的读写过程
  • 所有数据的读写都是通过Buffer来进行的,永远不会出现直接向Channel写入数据的情况,或是直接从Channel读取数据的情况(NIO)
  • Buffer是一种容器,有特定的原生类型
  • Buffer可以做到分类进行处理

9、关于NIO的Buffer 3个重要状态属性含义(重点

  • 图解关系
  • Buffer 3个重要状态
  • postion , limit , capacity (难点和重点)
  • 一个Buffer的capacity它包含的元素的数量,一个Buffer的capacity不可能为负数,并且永远不会变化
  • 一个Buffer的limit无法去写或读的,第一个元素的索引不可能是负数,不会超过capacity
  • 一个Buffer的position下一个元素索引的读或写不可能是负数,不会超过limit
  • 范围: capacity > limit > position
  • 范围: 0 <= mark <= position <= limit <= capacity

compact 方法
1、将所有未读的数据复制到Buffer起始位置处
2、将position 设为最后一个为读的元素的后面
3、将limit设为capacity
4、现在Buffer就准备好了,但是不会覆盖未读的数据

10、 clear ,flip ,rewind 方法(重点)

clear
1、clear 其实对数据重置,并没有清空数据(原理)
2、将limit值设为capacity
3、将postion值设为0

11、 直接缓冲区(重点)

  • 直接缓冲区,堆的缓冲,堆上的内存分配堆内上直接由JVM管理,堆外的内存分配,不由JVM管理,有操作系统进行管理
  • DirectByteBuffer直接缓冲,用了很多不开源的底层实现
  • DirectByteBuffer在java对,就不会存在一个所谓的数组,因为真实的数据已经在堆外存放者,所以用于数据读写,直接由操作系统来跟堆外的内存进行交互(少一次数据拷贝)称为零拷贝
  • MapperByteBuffe是一个直接缓冲区,是一个内存映射文件区域,用于内存映射的文件的内存本身在java堆面,换句话说,它是堆外内存,直接从内存修改,有操作系统操作
  • 拷贝不会产生GC,由JVM控制

DirectByteBuffer

12、 间接缓冲

  • 如果用了HeapByteBuffer实际上真正数据IO多了一个数据的拷贝过程,会把java的内存空间的字节数组的内容原封不动的拷贝到java模型内存之外的操作系统某块内存中,然后整个内存区字节跟我们IO设备进行交互
  • HeapByteBuffer

13、 中间缓冲区

  • 对于直接缓冲区Java的JVM就可以直接本地IO的操作,避免操作系统的原生的IO还复制内容到一个中间的缓冲区

14、 零拷贝

  • 零拷贝将IO操作时候不必你的Buffer的内容再拷贝一份放置系统的内存空间中,直接将你的堆上的分配的内存用于IO操作进行打交道,减少内存中转的过程,提供性能(重点

15、 通过NIO读取文件涉及到3个步骤

  • 1、从FileInputStream获取到FileChannel对象

  • 2、创建Buffer

  • 3、将数据从Channel读取到Buffer中

  • FileChannel关系图

16、 绝对方法和相对方法的含义

  • 相当方法:limit值与position值会在操作时被考虑到
  • 绝对方法:完成忽略掉limit值与position值

17、 ByteBuffer

  • ByteBuffer 放入是什么类型,取出来类型就是什么类型
  • ByteBuffer

18、 asReadOnlyBuffer 只读缓冲区

  • 只读Buffer,我们可以随时将一个普通的Buffer调用,asReadOnlyBuffer方法返回一个只读Buffer,但不能将一个只读Buffer转换为读写Buffer

19、 native关键字(难点)

  • native是本地的意思,通过JNI调用C / C++ 的底层代码

20、 堆上内存(难点)

  • new 处理的实例一定位于堆上(java内存模型)native不在java内存模型,称为堆外内存(难点
  • 堆上内存有JVM管控,堆外内存由操作系统进行管理,成员变量可以访问堆内/外内存,在Buffer有一个address变量直接可以访问直接缓冲

21、 Buffer的Scattering与Gathering(重点)

  • Scattering不仅传递一个Buffer,还可以传递一个Buffer的数组,其实将一个Channel里面的数据进行读取,如果不把第一个读完不会读第二个Channel的数据到Buffer数组
  • Gathering与Scattering刚好相反,它是写操作,一个一个地写到Buffer

22、 CPU上下文切换会影响性能

  • 系统的CPU不断切换的变化会影响线程的性能,而且查询设计使用太多线程会有弊端

23 推荐学习博客

作者:寅务