接着上面那篇JavaWeb有关的故事,讲讲NIO,上篇在这:JavaWeb有关的故事
几个IO事实:html
进程执行IO操做,要么把缓冲区的数据排干(read),要么用数据把缓冲区填满(write)。
进程使用read()系统调用,要求其缓冲区须要被填满,内核随机向磁盘控制硬件发出命令,经过DMA(无需主CPU协助)讲数据写入内核内存缓冲区,一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程read()调用时执行的缓冲区。java
其中用户空间和内核空间
:
用户空间是常规进程所在区域,如JVM。内核空间是操做系统所在区域,有特别权力,能与设备控制器通信,控制用户区域进程的运行状态等。总之,全部IO都直接或间接经过内核空间。
硬件不能直接访问用户空间,硬件设备操做的是固定大小的数据块,而用户进程请求的多是任意大小的或非对齐的数据块。缓存
使用虚拟地址取代物理内存地址,有个两大类好处,一是多个虚拟地址可知指向同一物理地址,二是虚拟内存空间可大于硬件内存。函数
文件系统与磁盘不一样,文件系统是高层次的抽象,是安排、解释磁盘或其余随机存取块设备数据的一种独特方法。
文件系统数据也会同其余内存页同样获得高速缓存,对于随后发生的IO请求,文件数据的部分或所有可能仍位于物理内存中,无需从磁盘读取。this
文件锁定
:分为共享和独占。多个共享锁可同时对同一文件区域发生做用,独占锁要求相关区域不能有其余锁起做用。操作系统
原理模仿通道,必须顺序存取,如控制台设备、打印机端口。code
Buffer类是nio构造基础,一个Buffer对象是固定数量的数据容器,在这里的数据可悲存储并在以后用于检索。能够理解为是I/O操做中数据的中转站。缓冲区直接为通道(Channel)服务,写入数据到通道或从通道读取数据,这样的操利用缓冲区数据来传递就能够达到对数据高效处理的目的。htm
容量capacity:建立时设定,不可改变。
上界limit:不可读写。现存元素的计数。
位置position:下一个要被读或写元素的索引。
标记mark:一个备忘位置。对象
java.nio.Buffer类是一个抽象类,不能被实例化。Buffer类的直接子类,如ByteBuffer等也是抽象类,因此也不能被实例化。
可是ByteBuffer类提供了静态工厂方法来得到ByteBuffer的实例,终于到了show代码的时候了=。=,以下:blog
//初始化 ByteBuffer buffer = ByteBuffer.allocate(1024); //数据填充 buffer.put((byte) 'H').put((byte) 'e').put((byte) 'l').put((byte) 'l').put((byte) 'o'); System.out.println(buffer.toString());//java.nio.HeapByteBuffer[pos=5 lim=1024 cap=1024] System.out.println((char) (buffer.get(0)));//H //数据修改 buffer.put(0, (byte) 'M').put((byte) 'w'); System.out.println(buffer.toString());//java.nio.HeapByteBuffer[pos=6 lim=1024 cap=1024] System.out.println((char) (buffer.get(0)));//M //flip将position归0,将一个待填充状态的缓冲区翻转成准备读出状态 System.out.println(buffer);//java.nio.HeapByteBuffer[pos=6 lim=1024 cap=1024] buffer.flip(); //buffer.rewind();//相似flip,可是不影响limit属性。java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024] System.out.println(buffer);//java.nio.HeapByteBuffer[pos=0 lim=6 cap=1024] //使用hasRemaining()判断是否达到缓冲区上界 while (buffer.hasRemaining()) { System.out.print((char) (buffer.get())); }//Mellow System.out.println(""); //压缩,compact丢弃已释放数据,保留未释放数据 buffer.compact(); //mark,在某个位置标记,reset( )函数将位置设为当前的标记值。 // 若是标记值未定义,调 用 reset( )将致使 InvalidMarkException 异常。 // 一些缓冲区函数会抛弃已经设定的标记 (rewind( ),clear( ),以及 flip( )老是抛弃标记)。 buffer.position(2).mark();
比较两个缓冲区,ByteBuffer已经实现Comparable接口,源码以下:
public int compareTo(ByteBuffer that) { int n = this.position() + Math.min(this.remaining(), that.remaining()); for (int i = this.position(), j = that.position(); i < n; i++, j++) { int cmp = compare(this.get(i), that.get(j)); if (cmp != 0) return cmp; } return this.remaining() - that.remaining(); } private static int compare(byte x, byte y) { return Byte.compare(x, y); }
//批量取 byte[] myArray = new byte[1000]; buffer.get(myArray); //等价于: buffer.get(myArray, 0, myArray.length); //批量存 byte[] myString = new byte[1000]; buffer.put(myString); //等价于: buffer.put(myString,0,myString.length);
使用duplicate()函数能够复制缓冲区,会建立一个新的buffer对象,但并不会复制数据,原始缓冲区和副本都会操做一样的数据元素。
全部的基本数据类型都有相应的缓冲区类(布尔除外),字节类型比较特殊,字节是操做系统和其IO设备使用的基本类型。
非字节类型的基本类型,也是由字节组合成的,好比char2个字节,int4个字节,double8个字节。组合的字节是有顺序的,Java默认的字节顺序是大端字节顺序,参见类ByteOreder。
字节缓冲区能够成为通道所执行IO的源头或目标,非字节缓冲区传递给通道,会隐含执行复制内容到一个临时ByteBuffer,因此直接缓冲区是IO的最佳选择。
经过isDirect()函数,断定缓冲区是否为直接缓冲区。
视图缓冲区,这种视图对象维持它本身的属性、容量、位置、上界和标记,可是和原来缓冲区共享数据元素。
CharBuffer charBuffer = byteBuffer.asCharBuffer( );
ByteBuffer类为每一种原始数据类型提供了存取和转化的方法,好比getInt()函数被调用,从当前位置开始的4个字节会被包装成一个int类型变量返回。
对于无符号数据,自行实现,注意精度问题。
以上来自天团运营总监:坤少