nio含义 non-blocking io 非阻塞IO
channel 数据双向通道
buffer 数据缓冲区
常见四种channel
1、FileChannel 文件数据传输通道
2、DatagramChannel 用于UDP的传输通道
3、SocketChannel 用于TCP 服务端客户端都可用
4、ServerSocketChannel 用于TCP 服务端专用
buffer
1、ByteBuffer(最常用)
1.1 MappedByteBuffer
1.2 DirectByteBuffer
1.3 HeapByteBuffer
2、ShortBuffer
3、IntBuffer
4、LongBuffer
5、FloatBuffer
6、DoubleBuffer
7、CharBuffer
背景:
1、多线程是提高效率方法,但是过多的话 CPU跟不上。内存占用高、线程上下文切换成本高、只适合连接数少的场景(一个网络连接看成一个线程)。
2、使用线程池的话可以通过限制线程数的方法改进1的问题。
3、但是使用线程池的话,在其阻塞模式下,线程仅能处理一个socket连接,导致线程利用率不高,仅适合短连接场景。
selector版的设计
为每个线程提供一个selector,selector监视了多个channel,selecot管理了多个channel,获取这些channel上发生的事件,channel工作在非阻塞模式下(与线程池最大的区别),不会让线程吊死在一个channel上,适合连接数特别多,但流量低的场景。
事件:可连接、可读、可写
main(){
//输入输出流
try(FileChannel channel = new FileInputStream("data.txt").getChannel()){
//准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
//从channel读取数据,想buffer写入
channel.read(buffer);
//切换到读模式
buffer.flip();
while(buffer.hasRemaining()){//是否还有剩余未读数据
byte b = buffer.get();//get(i)不会让position往后移,但get()会
System.out.println((char) b);
}
}catch(IOException e){
}
}
上面代码只能取到10个字符,这里是为了模拟读取多次数据故意这么写的,应为实际场景中allocate的缓冲区不可能分配无限大。
下面代码为循环使用缓冲
main(){
//输入输出流
try(FileChannel channel = new FileInputStream("data.txt").getChannel()){
//准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
while(true){
//从channel读取数据,想buffer写入
int len = channel.read(buffer);
if(len == -1){//没有内容了
break;
}
//切换到读模式
buffer.flip();
while(buffer.hasRemaining()){//是否还有剩余未读数据
byte b = buffer.get();
System.out.println((char) b);
}
buffer.clear();//切换为写模式
}
}catch(IOException e){
}
}
属性:
1、capacity 容量
2、position 位置(读到那个位置指针)
3、limit (写入限制,只允许写到哪个位置)
写模式下,向buffer写入数据其中limit等于caoacity
buffer.flip()后 切换为读模式,limit等于写入的最后一个字节的位置可限制只读到有写入数据的位置
clear后切换为写模式
compact方式切换为写模式,用于数据没读完的情况下,保留未读的数据,删除已读数据,且把未读数据往前移
如果没有调用buffer.flip()则从buffer无法读取数据
使用buffer.compact()切换到写模式会使未读数据前移,建前面已读的位置填满后面的,且写的位置会到前移的上次未读完数据的后一个字节
1、ByteBuffer.allocate(16) java堆内存,读写效率较低,收到垃圾回收的影响
2、ByteBuffer.allocateDirect(16) 直接内存,读写效率较高(因为会少一次数据拷贝),使用的是系统内存,不会收到垃圾回收的影响。缺点:因为使用的系统内存,所以内存分配比较慢,需要调用操作系统函数
int readBytes = channel.read(buf); // 从channel读,向buffer写
和
buf.put((byte)127);
int writeByte = channel.write(buf); //从buffer读,向channel写
和
byte b = buf.get();
get方法会让position读指针向后走,如果想重复读取数据
可以调用rewind方法将position重新置为0
或者调用get(int i)方法获取索引 i 的内容,它不会移动读指针
FileChannel只能工作在阻塞模式下
不能直接打开FileChannel,必须通过FileInputStream、FileOutputStream或者RandomAccessFile来获取FileChannel,它们都有getChannel方法。
通过FileInputStream获取的channel只能读
通过FileOutputStream获取的channel只能写
通过RandomAccessFile是否能读写根据构造RandomAccessFile时的读写模式决定
重要
transferTo函数:将from内容传到to里面
这个函数效率高,底层会利用操作系统的零拷贝进行优化