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个核心概念关系图如:(重点)
[图解关系
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个重要状态属性含义(重点
)
- 图解关系
- 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控制
12、 间接缓冲
- 如果用了HeapByteBuffer实际上真正数据IO多了一个数据的拷贝过程,会把java的内存空间的字节数组的内容原封不动的拷贝到java模型内存之外的操作系统某块内存中,然后整个内存区字节跟我们IO设备进行交互
13、 中间缓冲区
- 对于直接缓冲区Java的JVM就可以直接本地IO的操作,避免操作系统的原生的IO还复制内容到一个中间的缓冲区
14、 零拷贝
- 零拷贝将IO操作时候不必你的Buffer的内容再拷贝一份放置系统的内存空间中,直接将你的堆上的分配的内存用于IO操作进行打交道,减少内存中转的过程,提供性能(重点)
15、 通过NIO读取文件涉及到3个步骤
1、从FileInputStream获取到FileChannel对象
2、创建Buffer
3、将数据从Channel读取到Buffer中
16、 绝对方法和相对方法的含义
- 相当方法:limit值与position值会在操作时被考虑到
- 绝对方法:完成忽略掉limit值与position值
17、 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不断切换的变化会影响线程的性能,而且查询设计使用太多线程会有弊端