#普通IO与NIO区别?
IO:按流处理数据,速度慢;普通IO;
NIO:按块处理数据,速度快;通过通道channel和缓冲区buffer作为基础的面向块的IO处理;
#通道channel和缓冲区buffer是什么?
通道和缓冲区是NIO的核心对象;
channel是对原有IO包中流的模拟,到任何目的地的数据都需要通过channel对象;字节
buffer是一个容器对象,发送给一个channel的所有对象都必须首先放到buffer中,同样,从channel中读取的数据都要读到buffer中,是不能直接从channel中读取数据的,只能通过buffer;
buffer是一个数据,常用的是字节数组ByteBuffer,缓冲区还提供了对数据的结构化访问,可以跟踪系统的读写进程;
#通道channel是对旧的IO流的模拟,那么通道与旧的io流的区别在哪里?
channel是双向的,可以同时读写,unix底层操作系统通道也是双向的;
旧的IO流是单向的;
#NIO的文件读写
读取文件步骤:从FileInputStream获取channel,创建Buffer,将数据从channel读到buffer中;
读文件:
FileInputStream fin = new FileInputStream("demo.txt");
FileChannel fc = fin.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
fc.read(buffer);
写文件:
FileOutputStream fout = new FileOutputStream("demo.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);
#NIO读写时,我们不需要告诉通道要读多少数据到缓冲区中,每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据及还有多少空间可以容纳更多的数据,JDK中是如何设计和实现这个个机制的?(待深入研究.)
#缓冲区内部实现机制
缓冲区的两个重要实现组件:状态变量,访问方法;
每一种java基本类型的缓冲区都是对象类Buffer的子类;buffer有三个似有属性:
private int position = 0;//跟踪了向缓冲区中写入多少数据或者从缓冲区中读取了多少数据;
private int limit;//表明还有多少数据需要取出,或还有多少空间可以放入数据;
private int capacity;//该缓冲区的最大数据容量;
每个基本类型的缓冲区底层实上就是一个该类型的数组;例如ByteBuffer中的final byte[] nb;
从通道读取数据,实际上是将读取的数据放到该数组中,向通道中写入时,是将该数组中的数据写入到通道中;
#如何使用
使用缓冲区将数据从输入通道拷贝到输出通道:
while(true){
buffer.clear();
int r = fcin.read(buffer);
if(r==-1){
break;
}
buffer.flip();
fcout.write(buffer);
}
其中:clear(),flip()用于让缓冲区在读写之间切换;
#连网与异步IO
nio中的连网与nio中的其它操作无区别,仍然是依赖于通道和缓冲区,使用InputStream和OutputStream获取通道;
异步io调用不会阻塞。它的最大优势:允许同时根据大量的输入和输出执行IO,同步程序常常求助于轮询或者创建许多线程处理大量连接,使用异步IO,可以监听任何数量的通道上的事件,不用轮询,也不用额外的线程;
应用实例:基于非组赛io的服务器端的处理流程,它接收网络连接并向它们回响它们可能发送的数据。
private void execute () throws IOException { // 创建一个新的选择器
Selector selector = Selector.open();
// 打开在每个端口上的监听,并向给定的选择器注册此通道接受客户端连接的I/O事件。
for (int i = 0; i < ports.length; i++) {
// 打开服务器套接字通道
ServerSocketChannel ssc = ServerSocketChannel.open(); // 设置此通道为非阻塞模式
ssc.configureBlocking(false);
// 绑定到特定地址
ServerSocket ss = ssc.socket();
 InetSocketAddress address = new InetSocketAddress(ports[i]); ss.bind(address);
// 向给定的选择器注册此通道的接受连接事件
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Going to listen on " + ports[i]);
}
while (true) {
// 这个方法会阻塞,直到至少有一个已注册的事件发生。
// 当一个或者更多的事件发生时,此方法将返回所发生的事件的数量。 int num = selector.select();
// 迭代所有的选择键,以处理特定的I/O事件。
Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iter = selectionKeys.iterator();
SocketChannel sc;
while (iter.hasNext()) {
SelectionKey key = iter.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
// 接受服务器套接字撒很能够传入的新的连接,并处理接受连接事件。
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
sc = ssc.accept();
// 将新连接的套接字通道设置为非阻塞模式
sc.configureBlocking(false);
// 接受连接后,在此通道上从新注册读取事件,以便接收数据。
sc.register(selector, SelectionKey.OP_READ);
// 删除处理过的选择键
iter.remove();
System.out.println("Got connection from " + sc);
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
// 处理读取事件,读取套接字通道中发来的数据。
sc = (SocketChannel) key.channel();
 // 读取数据
int bytesEchoed = 0;
while (true) {
echoBuffer.clear();
int r = sc.read(echoBuffer);
if (r == -1) {
break;
}
echoBuffer.flip();
sc.write(echoBuffer);
bytesEchoed += r;
}
System.out.println("Echoed " + bytesEchoed + " from " + sc); // 删除处理过的选择键
iter.remove();
}
}
}
}
#使用allocate()分配缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
其中,allocate()方法分配一个具有指定大小的底层数组,同时包装到一个对象中,例如ByteBuffer;
#使用数组转换成缓冲区
byte array[] = new byte[1024];
ByteBuffer buffer = ByteBuffer.wrap(array);
#缓冲区如何分片
实际上就是创建一个子缓冲区,新旧缓冲区的一部分共享数据;
ByteBuffer buffer = ByteBuffer.allocate(10);
for(int i=0; i<buffer.capacity(); i++){
buffer.put((byte)i):
}
//分片
buffer.position(3);
buffer.limit(7);
ByteBuffer slice = buffer.slice();
#如何创建只读缓冲区,及作用?
调用缓冲区的asReadOnlyBuffer()方法,将缓冲区转化为只读,只读缓冲区可以保护数据,使数据不被修改;
#直接和间接缓冲区
#内存映射文件IO
#将文件的前1024个字节映射到内存
MappedByteBuffer mbf = fc.map(FileChannel.MapModel.READ_WRITE, 0 , 1024);
<pre>
#分散,聚集IO
读:
<pre lang="java">
long read(ByeteBuffer[] dst);
long read(ByteBuffer[] dst, in offset, int length);
写:
long write(ByeteBuffer[] dst);
long write(ByteBuffer[] dst, in offset, int length);
参考:《NIO学习总结.pdf》