NIO-简单入门

总要有一个梦想或大或小

共 2809字,需浏览 6分钟

 · 2021-03-01

前段时间在学习项目中加入了Dubbo框架,说实话,也就用一下,至于原理,是真的不懂多少。这不,当我想要了解Dubbo的原理时,出现了个陌生词汇Netty,这下好了,一个没搞懂,又来一个,那就学Netty呗,然鹅,是这么简单吗,Netty的底层是NIO,what?啥是NIO鸭,那从这篇开始,我就扒一扒NIO那些事,其实NIO还能往深了扒,什么计组、操作系统等,奈何我层次不够,暂且不扒了,等我学习下


先来说下传统IO(BIO)

传统的IO指的是平常用到的那些输入流/输出流、字节流/字符流等,IO是面向流的,单向的,IO的各种流都是阻塞的,即在进行read()和write()操作时,线程直接阻塞,该线程将不能做任何事情;当来了新的请求,就只能重新开一个线程来处理这个请求,但也同样会阻塞

3aa5b4f21c5968e0c24ea62e98e9ae00.webp


再说下NIO

NIO是在JDK1.4中新出现的内容,其作用和IO是一致的,但是实现方法和作用是不同的,它是面向缓冲区、双向操作的、非阻塞的IO

b200ea54f42119996bfdd864a63c8075.webp


IO和NIO的区别

IO
NIO
面向流
面向缓冲区
阻塞IO
非阻塞IO

Selector选择器

面向流与面向缓冲

IO是面向流进行操作的,即每次从流中读一个或多个字节,直到读取所有的字节,这些数据没有被缓存起来,需要一次性读取或写入,且在此过程中线程是阻塞状态;六种的数据不能移动,如果需要移动,需要将六种的数据放到缓冲区中
NIO是面向缓冲区的,即数据是被读取到缓冲区中,可以基于缓冲区对其中的数据进行移动等操作,但是需要判断该缓冲区中是否包含所需的数据,且需要保证当缓冲区内数据未处理完成时,不能被新的数据覆盖掉

阻塞IO与非阻塞IO

IO的各种流都是阻塞的,当一个线程调用流的read()或write()时,该线程会被阻塞,直到有一些数据被读取,或数据完全写入,期间不能干其他事情,CPU转去处理其他线程,假设一个线程监听一个端口,一天只会有几次请求进来,但是CPU不得不为该线程不断的做上下文切换,而且大部分切换都是以阻塞告终,这是极其浪费系统资源的
非阻塞IO,是一个线程在读操作时,如果没有数据可读,就什么都不会获取,而不是阻塞;写操作时,再将数据写入某个通道时,不需要等它完全写入,这个线程就可以去做其他事情了NIO通信是利用事件驱动机制,而不是监听机制,事件到了再触发,NIO线程之间通过wait、notify等方式通知,保证每次上下文切换都有意义,避免系统资源的浪费

选择器

Java NIO的选择器就是将每个请求通道都注册进来,然后对这些通道进行监视,由专门的线程来选择通道进行执行,这种选择机制,可以使一个单独的线程很容易的来管理多个通道


NIO的主要元素

通道(channel):标识打开到IO设备(如:文件、套接字)的链接,跟IO中的流差不多,负责传输

通道的主要实现类    FileChannel    SocketChannel    ServerSocketChannel    DatagramChannel通道的获取1)Java针对支持通道的类提供了getChannel()方法  本地IO:    FileInputStream/FileOutputStream    RandomAccessFile  网络IO:    Socket    ServerSocket    DatagramSocket(2)在JDK1.7中的NIO.2  针对各个通道提供了静态方法 open()(3)在JDK1.7中的NIO.2  的Files工具类的newByteChannel()

缓冲区(Buffer):Java NIO中的Buffer用于和NIO通道进行交互。数据是从通道读入缓冲区,从缓冲区写入到通道中的

作用:数据的操作主要是在缓冲区中处理,负责存储结构:底层是数组,用来存储不同数据类型的数据分类:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBufferAPI:  allocate():获取缓冲区  allocateDirect():分配直接缓冲区  put():存入数据到缓冲区中  get():获取缓冲区中的数据  flip():切换读模式  reset():恢复到mark位置  hasRemaining():查看缓冲区中是否还有可操作的数据  remaining():获取缓冲区中可操作的数据  compact():清除已经读过的数据  rewind():可重复读,将position置为0四个核心属性:  capacity:容量,表示缓冲区中最大存储数据的容量,一旦声明不能改变  limit:界限,表示缓冲区中可以操作数据的大小(limit后面的数据不能读写)  position:位置,表示缓冲区中正在操作数据的位置  mark:位置,标记的位置  0 < mark < position <= limit <= capacity直接缓冲区和非直接缓冲区  非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中  直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率获取直接缓冲区的方式:  ByteBuffer.allocateDirect  通道内存映射文件:    FileChannel inChannel = FileChannel.open(...)    MappedByteBuffer inMapperBuf = inChannel.map(...) 使用Buffer读写数据一般遵循以下四个步骤:    写入数据到Buffer    调用flip()方法    从Buffer中读取数据    调用clear()方法或者compact()方法

4c602252ce190364899554a1fecaa3aa.webp

选择器(Selector):管理所有的通道,根据选择键来对应处理通道

Selector可以监听的事件类型  读:SelectionKey.OP_READ  写:SelectionKey.OP_WRITE  连接:SelectionKey.OP_CONNECT  接收:SelectionKey.OP_ACCEPT监听多个事件可以用 “|” 位或操作符进行连接


今天就先说到这里,其实很多概念看下代码更容易理解,后面的话会对内容再进行补充,包括学习下netty,dubbo等原理


由于篇幅问题,文中不展示大篇幅的代码,示例代码均已上传到码云,如需更详细的了解,请自行下载代码进行测试

https://gitee.com/MaYunJerryLee/urmd-code-demo/tree/master/urmd-nio-demo


浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报