一、NIO总共分为三大部分:
1、缓冲区:Buffer是所有的xxxBuffer类的父类,MappedByyteBuffer:映射的类,主要是映射系统中的内存。
缓存区分为两种,一种是直接缓冲区,另一种是非缓冲区。
缓存区有Buffer,ByteBuffer,LongBuffer,IntegerBuffer,FloatBuffer,DoubleBuffer。
非直接缓冲区主要存放在jvm缓存区中来拷贝
直接缓冲区--存在物理内存中。
2、选择器:poll模型是windows里面的,epoll是linux系统中的,select,kqueue是操作系统的。整个selector在构建的时候不是由java来构建的,而是由java语言调用c语言来是实现的,它的底层是系统的底层。
3、通道:NIO最关联的就是通道,而通道要和选择器的事件进行绑定。
二、关于Buffer的代码如下:
1、创建ByteBuffer的对象,这里不是new的,而是创建该对象时要分配内存:allocate代表分配一块内存空间,将ByteBuffer的对象指向内存空间里面去。单位是字节,代码如下:
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
System.out.println("position:"+byteBuffer.position());
System.out.println("limit:"+byteBuffer.limit());
System.out.println("capacity:"+byteBuffer.capacity());
System.out.println("往bytebuffer里存放数据。。。");
byteBuffer.put("A23abc一二三".getBytes());
System.out.println("position:"+byteBuffer.position());
System.out.println("limit:"+byteBuffer.limit());
System.out.println("capacity:"+byteBuffer.capacity());
运行的结果如下:往缓存区里放数据只会导致position的大小会变,其他的不会变。
2、如何从缓存区中读数据
System.out.println("往bytebuffer里读取数据。。。");
byteBuffer.flip();// 开启读取模式
System.out.println("position:"+byteBuffer.position());
System.out.println("limit:"+byteBuffer.limit());
System.out.println("capacity:"+byteBuffer.capacity());
//应用程序的一个缓存
byte[] bytes = new byte[byteBuffer.limit()];
//读取到缓存区中
byteBuffer.get(bytes);
System.out.println(newString(bytes, 0, bytes.length));
运行的结果如下:这时position置为0,limit的值和上次的position的值一样,把position的值赋给limit。一旦缓冲区的容量定下来以后,那么缓冲区的容量是不可变的。读的数据就是从0的位置到15的位置。
3、rewind方法:
System.out.println("往bytebuffer里重复读取数据。。。" );
byteBuffer.rewind();
System.out.println("position:"+byteBuffer.position());
System.out.println("limit:"+byteBuffer.limit());
System.out.println("capacity:"+byteBuffer.capacity());
byte[] bytes2 = new byte[byteBuffer.limit()];
byteBuffer.get(bytes2);
System.out.println(newString(bytes2, 0, bytes.length));
运行的结果如下:rewind:重复读取,不加这个方法会报错,因为limit后面不能再读取数据了。这个方法的意思是重复读的意思。limit不会变,但是position会变为0。
4、clear方法:调用这个方法的意思又可以再进行写的操作。
System.out.println("清空缓存区。。。");
byteBuffer.clear();//// 值不清空 下标清空
System.out.println("position:"+byteBuffer.position());
System.out.println("limit:"+byteBuffer.limit());
System.out.println("capacity:"+byteBuffer.capacity());
System.out.println((char) byteBuffer.get());
运行的结果如下:
小结总结:上面的缓冲区是普通的缓冲区,直接放到了jvm当中的。而缓存区是不停的再读数据,会覆盖。
5、分散读取聚集写入
可以分开去读,写是在一起写。
注意:在分配指定大小的缓冲区的时候,如果是直接缓冲区的话是直接写在内存中,不是写在jvm的工作缓存区的。如果用分散读取聚集写入的话,不能用直接的缓冲区的方式,应该用普通的方式来分配。
代码如下:
// 随机访问
RandomAccessFile raf = new RandomAccessFile("d:/abc.txt", "rw");
// 获取通信
FileChannel channel = raf.getChannel();
// 分配指定大小指定缓冲区
//这两个缓冲区是连通的
//buf2相当于是备用缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(6);
ByteBuffer buf2 = ByteBuffer.allocate(10);
// 分散读取
ByteBuffer[] bufs = {buf1, buf2};
channel.read(bufs);
for (ByteBuffer buf : bufs) {
// 切换成读模式
buf.flip();
}
System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
System.out.println("-----------------------------");
System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
System.out.println("-----------重复读取-------------");
RandomAccessFile raf2 = new RandomAccessFile("d:/abc.txt", "rw");
// 获取通道
FileChannel channel2 = raf2.getChannel();
channel2.write(bufs);
raf2.close();
raf.close();
运行的结果如下:
关于管道的代码如下:
package com.gerry.nio.demo.nio.channel;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
/**
* 通道和流绑定
* 普通的缓冲区是基于jvm的,直接缓冲区是基于内存的。
*/
public class FileInputProgram {
static public void main( String args[] ) throws Exception {
FileInputStream fin = new FileInputStream("d:/abc.txt");
// 在jdk1.5之后可以通过文件的流对象获取通道
FileChannel fc = fin.getChannel();
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取数据到缓冲区
fc.read(buffer);
buffer.flip();
//判断是否已经读完
while (buffer.remaining() > 0) {
byte b = buffer.get();
System.out.print(((char)b));
}
fin.close();
}
}
运行的结果如下:
package com.gerry.nio.demo.nio.channel;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class FileOutputProgram {
static private final byte message[] = {'a','c','d','e'};
static public void main( String args[] ) throws Exception {
FileOutputStream fout = new FileOutputStream( "e:/abc.txt");
FileChannel fc = fout.getChannel();
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i=0; i<message.length; ++i) {
buffer.put(message[i]);
}
buffer.flip();
fc.write( buffer );
fout.flush();
fout.close();
}
}
运行的结果如下:
总结:上面的是关于nio的缓冲区和通道中常用的方法做了一个总结,明天继续写关于nio的selector的网络通信的应用以及netty的入门。