简述
三大核心组件:通道(channel),缓冲区(buffer),选择器(selector)
传统IO针对字节流或字符流进行操作,NIO基于channel和buffer进行操作,数据从通道channel读取到缓冲区buffer,或从缓冲区buffer写入到通道channel中。Selector用于监听多个通道的事件(打开连接,数据就绪),因此单个线程可以监听多个通道。
与IO的区别
IO NIO
面向流 面向缓冲区
阻塞 非阻塞
-- 选择器
通道(Channel)
既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
通道可以异步地读写。
通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
channel的实现
FileChannel:从文件中读写数据。
DatagramChannel:能通过UDP读写网络中的数据。
SocketChannel:能通过TCP读写网络中的数据。
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
缓冲区(Buffer)
常见类型:MappedByteBuffer CharBuffer DoubleBuffer FloatBuffer IntBuffer LongBuffer ShortBuffer
读写步骤:
1.写入数据到Buffer(记录写入多少数据)
2.调用flip()方法(开始读取,模式切换。写--读)
3.从Buffer中读取数据
4.调用clear()方法或者compact()方法(清空缓冲区)
Buffer重要属性:
capacity:作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。
position:写数据时,表示当前位置,初始值=0,最大值为capacity-1;当数据写入到buffer时,position会向前移动到可插入数据的buffer区;
读取数据时,也是从某个特定位置读取。当buffer从写切换到读模式下,position会重置为0,当从buffer的position处读取数据时,position会向前移动到下一个可读位置。
limit:写模式下,表示最多能往buffer离写多少数据,limit等于buffer的capacity的大小。
读模式下,表示你最多能去取到的数据。当切换buffer到读模式时,limit==写模式下的position。
分散(Scatter):从channel中读取操作时将数据写入到多个buffer中
聚集(Gather):写入操作时,将多个buffer中的数据聚集后发送到同一个channel中。
选择器(Selector)
是NIO中能够检测到一到多个通道,并且知道通道是否为类似读写事件做好准备的组件。 允许单线程处理多个通道
为了将Channel和Selector配合使用,必须将channel注册到selector上。通过SelectableChannel.register()方法实现。一起使用适合,channel必须处于非阻塞模式下,FileChannel不能切换到非阻塞模式,套接字通道都可以。
1、connect:客户端连接服务端事件,对应值为SelectionKey.OP_CONNECT
2、accept:服务端接收客户端连接事件,对应值为SelectionKey.OP_ACCEPT
3、read:读事件,对应值为SelectionKey.OP_READ
4、write:写事件,对应值为SelectionKey.OP_WRITE
当向Selector注册Channel时,register()方法会返回一个SelectionKey对象,该对象有以下属性:
interest集合:通过SelectionKey可以读写到该集合
ready集合:通道已经准备就绪的操作的集合。在一次选择(Selection)之后,你会首先访问这个ready set
Channel
Selector
附加的对象(可选)
通过Selector选择通道,可调用重载的select()方法:返回读事件已经就绪的 通道,返回的int值表示通道就绪数
int select():阻塞到至少有一个通道在你注册的事件上就绪了。
int select(long timeout):和select()一样,除了最长会阻塞timeout毫秒(参数)
int selectNow():不会阻塞,不管什么通道就绪都立刻返回
一旦调用select()方法,且返回值表明了有就绪通道,可通过selectedKeys()方法访问已选择的就绪通道。Set selectedKeys = selector.selectedKeys();
wakeUp() :处理阻塞在select()上的线程
close() :用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。