什么是NIO
NIO又叫New/Non-blocking IO,这个概念基本人人都听过,但是不一定每个人都懂他它的运行的原理。
这里我们来探讨这个问题,先用一个例子解释一下BIO到底阻塞了哪里。
/**
* 这是一个单线程BIOServer
* @author endless
* @create 2020-03-23
*/
public class BioServerDemo {
public static void main(String[] args) throws IOException {
// 创建ServerSocket,并绑定端口
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务启动成功");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("连接成功");
System.out.println("准备接收数据");
byte[] bytes = new byte[1024];
socket.getInputStream().read(bytes);
System.out.println("接收到了数据:" + new String(bytes));
}
}
}
/**
* BIO client
*
* @author endless
* @create 2020-03-23
*/
public class BioClientDemo {
public static void main(String[] args) throws IOException {
// 连接Server
Socket socket = new Socket("127.0.0.1", 9999);
System.out.println("连接成功");
Scanner scanner = new Scanner(System.in);
// 循环等待输入消息
while (true) {
String str = scanner.next();
// 约定退出口令
if ("exit".equalsIgnoreCase(str)) {
socket.close();
System.exit(0);
}
socket.getOutputStream().write(str.getBytes());
socket.getOutputStream().flush();
}
}
}
先运行Server

命令行打印服务启动成功,此时并无客户端连接,所以连接成功并未打印,说明程序被阻塞在了serverSocket.accept()方法
此时运行Client,Server打印日志连接成功和准备接收数据,此时Client尚未发送数据,Server被阻塞在socket.getInputStream().read(bytes)上,因此其他客户端无法进行连接。

在Client输入Hello回车,此时Server打印接收到了数据:Hello,说明客户端的连接发送过来数据了,此时服务端线程才解阻塞,在这个情况下,这个Server没有办法处理并发,同时期只能处理一个连接。
那么BIO是如何实现并发呢?答案也很明显,就是使用多线程,我们对Server进行一些小改动。
/**
* 这是一个BIOServer
* @author endless
* @create 2020-03-23
*/
public class BioServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务启动成功");
while (true) {
Socket socket = serverSocket.accept();
new Thread(()->{
System.out.println("连接成功");
System.out.println("准备接收数据");
byte[] bytes = new byte[1024];
try {
socket.getInputStream().read(bytes);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("接收到了数据:" + new String(bytes));
}).start();
}
}
}
使用子线程来对接收到的Socket进行处理,这样每个连接都被阻塞在单独的线程上,就可以实现并发访问Server。
总结:BIO的阻塞有两个地方:accept()和read(),并且BIO的并发只能通过多线程。
但是这里会有一个问题,就是如果绝大部分的连接都没有进行数据传输,只是建立了连接,这样就会产生很多无效的线程,而线程又是非常宝贵的稀缺资源,这样就会白白损失很多的性能,这也是BIO最大的性能瓶颈。
那能不能只用一个线程就能实现并发并且处理全部的连接呢?是否能设计一个Api,让accept和read不再阻塞,使用一个线程就能处理并发连接呢?答案是肯定的,这里就要用到我们的今天的主角NIO了。
NIO在JDK中被封装在了一个新的类中,我们先来写一个例子,这个例子实现了使用单线程来处理多连接。
/**
* NIO Server Demo
*
* @author endless
* @create 2020-03-23
*/
public

本文深入探讨了NIO(非阻塞IO)的工作原理,对比BIO(阻塞IO),并详细讲解了Epoll在Linux系统下如何实现高效的多路复用,通过Java NIO API与操作系统底层交互,实现单线程处理并发连接,极大提升网络应用性能。
最低0.47元/天 解锁文章
585

被折叠的 条评论
为什么被折叠?



