NIO很少公司在用,大多是在用neety了,感觉已经被遗弃了,这段时间,整理一下NIO相关方面的知识,结合自己做的小游戏,用它来介绍NIO如何使用,会介绍基础原理,主要以实例为主。
IO简单介绍:
IO,简单的说,就是把数据移进或移出缓冲区。JVMA进程执行IO操作,要求缓区被填满,就会间接调用系统命令read(),内核会向磁盘控制器发出命令,请求读取磁盘据,这一步由DMA完成。当磁盘控制器将缓冲区填满时,内核会把数据从内核缓冲区拷贝到IO进程,执行read调用时指定的缓冲区.如下图:
用户空间:常规进程所在的区域。JVM就在用户空间,在该区域执行的代码不能直接访问硬件设备。
内核空间:操作系统所在的区域。能与设备管理器通讯,控制用户进程的运行状态等。
NIO提供的新抽象:
ByteBuffer:字节缓冲区
属性介绍:
容量(Capacity)::缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
上限(Limit):缓冲区中现存元素的计数。通道会从position位置开始获取,取到limit位置结束.
位置(Position):元素索引。位置会自动由相应的get()和put()方法更新。
标记( MarK ) :一个备忘位置。
这四个属性遵循以下关系:
0 <= mark <= position <= limit <= capacity
例子:
ByteBuffer bb = ByteBuffer.allocate(Short.SIZE / 8 + Integer.SIZE / 8); System.out.println("填充前:"); System.out.println("capatity:" + bb.capacity()); System.out.println("position:" + bb.position()); System.out.println("limit:" + bb.limit()); bb.putShort((short) 1); bb.putInt(2); System.out.println("填充后:"); System.out.println("capatity:" + bb.capacity()); System.out.println("position:" + bb.position()); System.out.println("limit:" + bb.limit()); /*** * 填充前: * capatity:6 * position:0 * limit:6 * * 填充后: * capatity:6 * position:6 * limit:6 */
方法介绍:
put()/get():存取。
flip():翻转
compact():压缩
下面是代码模拟:
ByteBuffer bb = ByteBuffer.allocate(Short.SIZE / 8 + Integer.SIZE / 8);
System.out.println("填充前:");
System.out.println("capatity:" + bb.capacity());
System.out.println("position:" + bb.position());
System.out.println("limit:" + bb.limit());
bb.putShort((short) 1);
bb.putInt(2);
System.out.println("填充后:");
System.out.println("capatity:" + bb.capacity());
System.out.println("position:" + bb.position());
System.out.println("limit:" + bb.limit());
// 将位置值重新设为0,通道就会从正确位置开始获取
bb.flip();
System.out.println("翻转后:");
System.out.println("capatity:" + bb.capacity());
System.out.println("position:" + bb.position());
System.out.println("limit:" + bb.limit());
/***
* 填充前: capatity:6 position:0 limit:6 填充后: capatity:6 position:6 limit:6
* 翻转后: capatity:6 position:0 limit:6
*/
bb.getShort();
System.out.println("get后:");
System.out.println("capatity:" + bb.capacity());
System.out.println("position:" + bb.position());
System.out.println("limit:" + bb.limit());
// 有时,您可能只想从缓冲区中释放一部分数据,而不是全部,然后重新填充
bb.compact();
System.out.println("compact后:");
System.out.println("capatity:" + bb.capacity());
System.out.println("position:" + bb.position());
<p> System.out.println("limit:" + bb.limit());</p>
/***
* 填充前: capatity:6 position:0 limit:6
* 填充后: capatity:6 position:6 limit:6
* 翻转后: capatity:6 position:0 limit:6
* get后: capatity:6 position:2 limit:6
* compact后: capatity:6 position:4 limit:6
*/
Chnnel:通道
通道是双向的,可以读也可以写。常用的有SokcetChannel,ServerSocketChannel.
ServerSocketChannel:是一个基于通道的socket监听器.ServerSocketChannel没有bind( )方法,可以通过调用socket()方法来获取ServerSocket对象,用这个对象去绑定地址,然后ServerSocketChannel调用accept()方法等待连接。
SocketChannel:扮演客户端向服务器发起连接。open()方法会创建一个SocketChannel对象,调用connect()来将该socket通道连接。
Selector:选择器
选择器管理着被注册通道集合的信息和他们就绪的状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态.选择器用来管理通道。
SelectionKey:选择键
选择键封装了特定的通道与特定的选择器的注册关系.通道会调用register方法去注册选择器,并注册相应的选择键。通道和选择器的关系封装在选择键里.
这一篇就讲到这里,下 一篇将用到这些知识。