JAVA高并发
2021-05-07 15:51:15 0 举报
AI智能生成
java IO高并发基础原理
作者其他创作
大纲/内容
IO底层原理
IO读写原理
内存空间(虚拟内存)
内核空间(Kernel-Space)
内核模块运行此处,进程处于内核态
可以调用系统一切资源
用户空间(User-Space)
用户程序运行此处,处于用户态
只能执行简单的运算,不能直接调用系统资源
每个应用程序有独立的用户空间
用户进程态不能直接访问内核空间数据,也不能直接调用内核函数
应用程序的IO操作实际上不是物理设备级别的读写,而是缓存的复制
上层应用使用read系统调用时,仅仅把数据从内核缓冲区复制到应用的缓冲区(进程缓冲区)
上层应用使用write系统调用时,仅仅把数据从应用的缓冲区复制到内核缓冲区
IO模型
概念
阻塞与非阻塞
阻塞IO指的是需要内核IO操作彻底完成后才返回到用户空间执行用户程序的操作指令
同步与异步
同步IO是指用户空间(进程或者线程)是主动发起IO请求的一方,系统内核是被动接收方。异步IO则反过来,系统内核是主动发起IO请求的一方,用户空间是被动接收方。
同步阻塞IO(Blocking IO)
用户空间(或者线程)主动发起,需要等待内核IO操作彻底完成后才返回到用户空间的IO操作
过程
从Java进行IO读后发起read系统调用开始,用户线程(或者线程)就进入阻塞状态
当系统内核收到read系统调用后就开始准备数据。一开始,数据可能还没有到达内核缓冲区(例如,还没有收到一个完整的socket数据包),这时内核就要等待
内核一直等到完整的数据到达,就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到用户缓冲区中的字节数
直到内核返回后用户线程才会解除阻塞的状态,重新运行起来
优点
应用程序开发非常简单
在阻塞等待数据期间,用户线程挂起,基本不会占用CPU资源
缺点
每个连接配备一个独立的线程,一个线程维护一个连接的IO操作,在并发量小的情况下,这样做没有什么问题
在高并发的应用场景下,阻塞IO模型需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大,性能很低,基本上是不可用的
同步非阻塞IO(Non-Blocking IO,NIO)
用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间去执行后续的指令,即发起IO请求的用户进程(或者线程)处于非阻塞状态,与此同时,内核会立即返回给用户一个IO状态值
过程
在内核数据没有准备好的阶段,用户线程发起IO请求时立即返回(为了读取最终的数据,用户进程(或者线程)需要不断地发起IO系统调用)
在内核缓冲区中有数据的情况下,在数据的复制过程中系统调用是阻塞的,直到完成数据从内核缓冲区复制到用户缓冲区
复制完成后,系统调用返回成功,用户进程(或者线程)可以开始处理用户空间的缓冲区数据
优点
在内核等待数据过程中可以立即返回,用户线程不会阻塞,实时性好
缺点
需要不断轮询内核,大量占用CPU时间,效率低下
区别于Java中的NIO(New IO)
IO多路复用模型
IO多路复用(IO Multiplexing)
异步阻塞IO
Reactor模式
流程
选择器注册。首先,将需要read操作的目标文件描述符(socket连接)提前注册到Linux的select/epoll选择器中,在Java中所对应的选择器类是Selector类。然后,开启整个IO多路复用模型的轮询流程
就绪状态的轮询。通过选择器的查询方法,查询所有提前注册过的目标文件描述符(socket连接)的IO就绪状态。通过查询的系统调用,内核会返回一个就绪的socket列表。当任何一个注册过的socket中的数据准备好或者就绪了就说明内核缓冲区有数据了,内核将该socket加入就绪的列表中,并且返回就绪事件
用户线程获得了就绪状态的列表后,根据其中的socket连接发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区
复制完成后,内核返回结果,用户线程才会解除阻塞的状态,用户线程读取到了数据,继续执行
优点
一个选择器查询线程可以同时处理大量网络连接,不必创建大量线程,大大减轻系统开销
缺点
读写过程是阻塞的,如果要彻底解除线程阻塞,需要使用异步IO
一个用户进程(或者线程)可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核就能够将文件描述符的就绪状态返回给用户进程(或者线程),用户空间可以根据文件描述符的就绪状态进行相应的IO系统调用
异步IO(Asynchronous IO,AIO)
用户空间的线程变成被动接收者,而内核空间成为主动调用者
当用户线程收到通知时,数据已经被内核读取完毕并放在了用户缓冲区内,内核在IO完成后通知用户线程直接使用即可
类似于Java中典型的回调模式,用户进程(或者线程)向内核空间注册了各种IO事件的回调函数,由内核去主动调用
Java NIO
核心组件
Buffer(缓冲区)
非线程安全类
Buffer的子类会拥有一块内存,作为数据的读写缓冲区
重要属性
capacity(容量)
标识容量大小
初始化后不可改变
原因是:Buffer类的对象在初始化时会按照capacity分配内部数组的内存,在数组内存分配好之后,它的大小就不能改变了
capacity并不是指内部的内存块byte[]数组的字节数量,而是指能写入的数据对象的最大限制数量
position(读写位置)
当前操作的位置
limit(读写的限制)
可以写入或者读取的数据最大上限
在写模式下,limit属性值的含义为可以写入的数据最大上限。在刚进入写模式时,limit的值会被设置成缓冲区的capacity值,表示可以一直将缓冲区的容量写满
在读模式下,limit值的含义为最多能从缓冲区读取多少数据
mark(标记)
在缓冲区操作过程当中,可以将当前的position值临时存入mark属性中
需要的时候,再从mark中取出暂存的标记值,恢复到position属性中,重新从position位置开始处理
重要方法
allocate()
需要获取一个Buffer实例对象时,并不是使用子类的构造器来创建,而是调用子类的allocate()方法
一个缓冲区创建后
处于写模式
position为0
capacity的值是allocate参数值
limit也是allocate参数值
put()
把对象写入缓冲区
flip()
写模式到读模式翻转
过程
limit属性被设置成写模式时的position值,表示可以读取的最大数据位置
position由原来的写入位置变成新的可读位置,也就是0,表示可以从头开始读
get()
读取元素
读操作改变position值,在position值和limit值相等时,表示所有数据读取完成
当position指向了一个没有数据的元素位置,已经不能再读了,此时再读就会抛出BufferUnderflowException异常
rewind()
倒带,重新从头读取
position重置为0
limit保持不变
mark被清理,表示之前的临时位置不能再用了
mark()和reset()
mark()方法将当前position的值保存起来放在mark属性中,让mark属性记住这个临时位置
reset()方法将mark的值恢复到position中
clear()
读模式下,调用clear()方法将缓冲区切换为写模式
position清零
limit设置为capacity
Channel(通道)
一个通道可以表示一个底层的文件描述符,例如硬件设备、文件、网络连接等
一个通道可以被注册到多个选择器上
FileChannel
阻塞式,不能设置为非阻塞式
没有实现SelectableChannel
SocketChannel
支持阻塞和非阻塞两种模式
socketChannel.configureBlocking(false)设置为非阻塞模式
Selector(选择器)
能够选出(select)所监控的通道已经发生了哪些IO事件,包括读写就绪的IO操作事件
通道和选择器之间的关联通过register(注册)的方式完成。调用通道的Channel.register(Selectorsel,int ops)方法,可以将通道实例注册到一个选择器中
可供选择器监控的通道IO事件类型
可读:SelectionKey.OP_READ
SocketChannel通道有数据可读
可写:SelectionKey.OP_WRITE
SocketChannel通道等待数据写入
连接:SelectionKey.OP_CONNECT
SocketChannel完成了和对端的三次握手过程
接收:SelectionKey.OP_ACCEPT
ServerSocketChannel服务器连接监听通道,监听到一个新连接到来时
SelectableChannel
并不是所有的通道都是可以被选择器监控或选择的,只有继承了SelectableChannel的才可以被选择
使用流程
获取选择器实例
将通道注册到选择器实例
注册到选择器的通道必须处于非阻塞模式下,否则将抛出IllegalBlockingModeException异常
选出感兴趣的IO就绪事件(选择键集合)
select():阻塞调用,直到至少有一个通道发生了注册的IO事件
select(long timeout):和select()一样,但最长阻塞时间为timeout指定的毫秒数
selectNow():非阻塞,不管有没有IO事件都会立刻返回
Reactor模式
角色
Reactor线程
负责响应IO事件,并且分发到Handlers处理器
Handlers处理器
非阻塞的执行业务处理逻辑
0 条评论
下一页