通俗易懂学习NIO(一)
通俗易懂学习NIO(一)
NIO和BIO最大的区别就是IO是面向流的,NIO面向缓冲区的。而且NIO一个很大的改进是可以通过native
函数直接在系统内存区分配内存,避免了在用户进程和系统进程间来回的复制数据引起的效率问题。BIO是同步阻塞的,NIO的同步非阻塞的。
要理解NIO需要理解阻塞、非阻塞、同步和非同步的概念。
1.阻塞和非阻塞
阻塞和非阻塞是进程在访问数据的时候,数据是否准备就绪时的一种处理方式。
- 阻塞:当数据没有准备好的时候,往往需要等待数据准备好过后才能处理其他事情,否则一直等待在那里,BIO中的
read()
,write()
,accept()
就是阻塞的。
- 非阻塞:数据没有准备好,直接返回,不需要等待。
2.同步和非同步
我理解的同步和异步是基于应用程序和操作系统处理IO时间所采用的方式不同。
同步:应用程序直接参与IO读写的操作
异步:所有的IO读写交给操作系统去处理,应用程序并不需要去完成真正的IO操作,当操作系统完成IO操作后,给应用程序一个通知就行了。
3.BIO和NIO的IO模型
BIO是同步阻塞式IO模型,要实现多个IO操作的快速响应,一般让读写方法加入到线程里面,但是这样对线程的性能开销比较大。
NIO是多路复用IO模型,参考linux的
select
的灵感实现同步非阻塞IO,IO事件交给一个单独的线程来处理,去用这个线程轮询IO事件,事件发生时,通知相应的读写线程,这样的话,阻塞不是所有的IO线程,只需要阻塞一个线程就行了.
一个易于理解但有点牵强的例子: 餐厅吃饭,每个客人点完菜不是立刻就能吃上的,需要厨师烹制和排队等待时间,假如顾客只能呆在厨房边干等着自己菜,不能干其他事情,这样当然是不现实的,特别是顾客越多,厨房越拥挤(对应于BIO)
所以现实中,都会有服务员这一角色,顾客只要坐在桌上等着就行了,可以玩手机、聊天,服务员会不断的到厨房看着哪桌的菜煮了直接就端上去(对应于NIO)
4.java NIO的API
java NIO有3个核心组件Buffer
、Channel
、Selector
,这里先只介绍一下Buffer
Buffer是存储基本类型数据的容器。学习Buffer,需要理解Buffer的4个关键属性
- capacity (总容量)
- position (指针当前位置)
- limit (读/写边界位置)
- mark (标记位置)
Buffer是一个接口,基本的8种数据除了boolean都对应一种Buffer,如ByteBuffer
,CharBuffer
初始化一个CharBuffer,调用allocate(capacity)
CharBuffer buffer = CharBuffer.allocate(8);
初始化后,4个属性的含义如下
当调用put()
,get()
时,position指针位置就会向前移动
buffer.put("Hello");
System.out.println(buffer.position());
mark()
方法,mark指针移动到当前位置
buffer.mark();
buffer.put("g");
limit()
会把limit指针移动到当前位置或者指定位置,给limit,capacity之间的位置放置数据会抛出BufferUnderflowException
异常
buffer.put("g");
buffer.limit();
buffer.put("a");
clear()
把position设为0,limit设为capacity,并且清除mark标记。(写入新数据)
buffer.reset();
flip()
把limit设为当前position,把position设为0,同时清除mark标记(转写为读)
buffer.put("Hellog");
buffer.flip();
rewind()
position设为0,limit不变,同时清除mark标记。(数据重写)
buffer.put("Hellog");
buffer.rewind();
compact()
将 position 与 limit之间的数据复制到buffer的开始位置,且 position = limit -position,limit = capacity,同时清除mark标记,但如果position 与limit 之间没有数据的话,就不会进行复制(常用于防止写出到Channel不完整)
buffer.put("Hellog");
buffer.limit();
buffer.compact();
reset()
将position恢复到mark的位置上。即position=mark,且不清除mark标记
buffer.put("Hello");
buffer.mark();
buffer.put("gay");
buffer.reset();
Channel、Selector 见(二)