浅显易懂的Java NIO

1 篇文章 0 订阅
1 篇文章 0 订阅

初学nio,之前很多文章都看不懂,最近终于开窍了一点,赶紧记录下。本文适合小白,也欢迎各位大佬批评指正哟。

一、推荐教程

推荐并发编程网的nio译文教程:

(1)概述:http://ifeve.com/overview/

(2)channels:http://ifeve.com/channels/

(3)buffers:http://ifeve.com/buffers/

(4)scatter/Gather:http://ifeve.com/java-nio-scattergather/

(5)数据传输:http://ifeve.com/java-nio-channel-to-channel/

(6)selector:http://ifeve.com/selectors/

(7)FileChannel:http://ifeve.com/file-channel/

(8)ScoketChannel:http://ifeve.com/socket-channel/

(9)ServerSocketChannel:http://ifeve.com/server-socket-channel/

(10)DatagramChannel:http://ifeve.com/datagram-channel/

(11)Pipe:http://ifeve.com/pipe/

(12)IO NIO对比:http://ifeve.com/java-nio-vs-io/

(13)NIO path:http://ifeve.com/java-nio-path/

(14)AsynchronousFileChannel:http://ifeve.com/java-nio-asynchronousfilechannel/

原文教程:http://tutorials.jenkov.com/java-nio/index.html

二、nio概述

要学习一项技术之前,首先来思考三个问题,是什么?为什么?怎么用?

(1)nio是什么?

non-blocking io,非阻塞的io。其实java的nio是同步非阻塞io。那么什么是阻塞io和非阻塞io呢?什么是同步io和异步io呢?

比如说已知要读取10个字节的数据,但是由于某些原因只能发送5个字节过来。

io模式获取数据过程
阻塞io收到5个字节数据,但是我知道一共有10个字节的数据,就一定要等另外5个字节来了再一起返回。
非阻塞io只收到5个字节数据,那我就只返回5个字节吧,剩下的下次再来拿。
同步iojava自己进行处理
异步iojava委托操作系统进行处理,等到系统处理完了,会调用java的回调函数进行通知

关于阻塞io和非阻塞io可以参考 https://blog.csdn.net/qq_34638435/article/details/81878301

显而易见,非阻塞io很耗时,如果数据迟迟没有准备出来就会一直等待。但是每次拿到的数据是完整的。非阻塞io返回快,但是数据可能不完整。

由于java的bio和nio都是同步的io,此处不过多展开,等待后面学习aio时在进行展开比较。

(2)为什么要用nio?使用场景有哪些?

看了上面的例子之后,你可能会觉得使用nio是因为它快,但事实并非如此,使用nio的主要原因是为了节约服务器的资源。

比如你要开发一个网站,使用阻塞io的话,对于客户端的一个请求A,你需要分配给他一个线程A。由于它只会等数据准备完成再返回,你的线程A只能服务于客户请求A。这时候来了请求B,即使线程A正在等待数据,什么也没干,也不能使用线程A了,只能再开个线程B给请求B使用,这就是一请求一线程的情况。如果请求量大了,服务端的线程池就会耗尽。同时线程间的切换需要消耗服务器的内存和cpu资源。

当然还有其他我还不太能理解的原因,请参考:https://www.cnblogs.com/wuyida/archive/2012/12/29/6301060.html

下面就进入了如何使用nio的环节。

三、nio核心组件

推荐教程里面讲了很多东西,其实nio的核心只有这三个Buffer,Channel,Selector。

(1)Channel

字面意思就是通道,通道的一端是缓存(Buffer),另外一端可以是文件,网络套接字,或者是TCP,UDP的网络连接。我们可以类比IO中的stream,区别是stream是单向的,比如InputStream只能输入,OutputStream只能输出,而Channel是双向的。

针对数据来源的不同,Channel有多种实现:

Channel的实现描述
FileChannel从文件获取数据
DatagramChannel通过UDP获取网络中的数据
SocketChannel通过TCP获取网络中的数据
SocketServerChannel可以监听新进的TCP连接的通道,类似SocketServer

 

常见函数:

函数解释数据流
channel.write(buffer)channel中的数据写入bufferbuffer ---> channel
channel.read(buffer)从buffer中读取数据到channelchannel ---> buffer

我个人之前是分不清channel的read和write函数的,这里想个办法来区分这两个方法的数据走向。首先联想我们自己,读和写指的是什么?读就是外部信息输入到大脑中,写就是大脑中的信息输出到外界。类比如下:

大脑 ---> CPU

外围组件 ---> channel

类比读操作信息流写操作数据流
大脑外部信息 ---> 大脑大脑  ---> 外部
CPUchannel ---> CPUCPU ---> channel

buffer相比磁盘等外部来说,属于内部。

(2)Buffer
就是一块被包装的内存,可以从channel获取数据,也可以把数据写入channel。针对不同的java类型有不同的包装类,如ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer。还有操作大文件的MappedByteBuffer。

buffer中常见字段:capacity,position,limit

比较好理解,capacity为容量,创建buffer的大小。读取buffer中数据时,position为可读数据的开头,limit为数据的结尾;向buffer写入数据时,position初始为0,写入一定数据后,position向后移位。

常见函数

常见函数解释
buffer.flip()写模式转为读模式的翻转
buffer.clear()清空buffer,position置为0
buffer.compact()清除读过的数据,position置为未读的位置
buffer.rewind()重新读数据,position置为0

 

个人总是记不住flip是什么模式转为什么模式,记忆方法如下:

flip这个词是翻转的意思,当你创建一个buffer的时候,他一定是空的,所以开始是处于读模式的,模式翻转就变成了写模式。因此flip是读模式转为写模式。

(3)Selector

我们可以把selector比喻成快餐店的前台店员,每个顾客和的厨师可以类比为channel,排队的顾客向selector注册了点餐事件,当点完餐后顾客向selector注册了取餐事件,厨师向selector注册了做饭事件。selector处理了排队的点餐顾客后,会通知后台制作的店员,当轮训时发现厨师做好了饭,会通知等餐的顾客。可以抽象为下图:即一个selector可以处理多个channel,channel向selector注册感兴趣的事件,selector会轮训这些事件,当某事件准备好时,会通知对应的channel。

这样原本一请求一线程的模式可以由一个线程来完成,节省了服务器资源。

四、完整代码解析

待完成。

 

参考文章:

(1)推荐教程的文章

(2)一文让你彻底理解 Java NIO 核心组件:  https://segmentfault.com/a/1190000017040893

(3)阻塞IO与非阻塞IO: https://blog.csdn.net/qq_34638435/article/details/81878301

(4)NIO:为什么需要 NIO?https://www.cnblogs.com/wuyida/archive/2012/12/29/6301060.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值