NIO三大核心组件
Selector:之前没有Selector时,多线程时一个客户端对应一个线程会造成上下文切换频繁,占用CPU资源,内存占用高,适合连接数较少。使用线程池时,仅适用短链接场景,阻塞模式下只能处理一个连接。有了Selector之后,一个线程对应一个Selector对应多个连接。Selector监控事件的发生,交给线程处理。是非阻塞的。适用于连接数多,但是流量不高的情况。
Buffer:可以从Channel读数据,也可以向Channel写数据,用来缓冲读写数据,外部数据一定是先经过Buffer
Channel:类似于Stream,但他是双向的。可以从Buffer读数据,也可以向Buffer写数据,常见的Channel有
FileChannel; DatagramChannel; //UDP SocketChannel; ServerSocketChannel;
ByteBuffer
读写案例
从IO模型到Netty笔记(一)_明天一定.的博客-CSDN博客IO模型,NIO三大核心https://blog.csdn.net/wai_58934/article/details/123145720
注意
使用时要注意读写切换
buffer.flip(); //切换读 buffer.clear(); //切换写 buffer.rewind(); //反复读取时,从头读 buffer.get(); //一次拿一个字节
模拟解决黏包问题
public class Get {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(32);
buffer.put("Hello\nI'm lt\nWh".getBytes());
split(buffer);
buffer.put("o are you\n".getBytes());
split(buffer);
}
private static void split(ByteBuffer buffer) {
buffer.flip();
for (int i = 0; i < buffer.limit(); i++) {
if(buffer.get(i)=='\n'){
int len = i-buffer.position()+1;
ByteBuffer allocate = ByteBuffer.allocate(len);
for (int j = 0; j < len; j++) {
allocate.put(buffer.get());
}
System.out.print(new String(allocate.array()));
}
}
buffer.compact();
}
}
byteBuffer涉及到分包的问题,自己处理比较麻烦,建议使用netty封装好的类
NIO遍历文件夹
简单案例,根据业务重写SimpleFileVisitor的方法即可。
public static void main(String[] args) throws IOException {
Files.walkFileTree(Paths.get("D:\\xxxxxxxxxxxxxxxxxxx"), new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file.getFileName());
return super.visitFile(file, attrs);
}
});
}
发展历程
- 接收连接和读取数据是阻塞的
- 轮询是否有连接和是否有数据发送,非阻塞,但是一直在轮询
- selector出现后,它和channel联系,当有事件发生时才去调用(当事件未处理时,仍会一直调用)处理完key之后要及时移除。如果有耗时长的,影响整体效率。
- selector的多线程,一个负责连接事件,一些负责读写事件。
处理断开异常可以捕获异常并使用key.cancle()取消事件。
处理正常断开可以判断channel.read(buffer)返回值是否为-1,然后再取消。
每个channel可以注册一个附件,并用key.attachment()拿到。
两线程确定执行顺序解决方案之一是使用线程安全的队列,先把线程放入队列,等待该执行时机取出并执行。