1.概览
- BIO(Blocking I/O)就像你亲自去咖啡店点一杯咖啡,然后等待咖啡师傅为你泡制咖啡。在这个过程中,你需要一直等待,不能做其他事情,知道咖啡师傅完成工作并将咖啡交给你。这中方式非常耗时,而且你需要专门的人员为每个人制证咖啡。
- NIO(Non-Blocking I/O)就像在咖啡店里使用自助咖啡机。你可以插入咖啡杯,按下按钮开始制作咖啡,然后继续等待。当咖啡准备好时,机器会通知你,你可以拿起咖啡杯,在这个过程中,你可以做其他事情,只要不断关注咖啡机是否完成。
- AIO(Asynchronous I/O)就像你再咖啡订购应用中预想下单并设置好提醒。
2.区别
它们有以下区别:
2.1.阻塞/非阻塞
BIO是阻塞式的I/O模型,即在进行I/O操作时,线程会一直阻塞等待数据的读取或者写入。
NIO是非阻塞式的I/O模型,它通过使用选择器(Selector)和通道(Channel)的概念,使得线程在进行I/O操作时可以立即返回,而不需要一直等待数据就绪。
AIO也是非阻塞的,但相较于NIO,它更加高级和异步,可以通过回调机制在操作完成时通知应用程序。
2.2.同步/异步
BIO是同步的,意味着线程会等待I/O操作的完成。
NIO是同步或者异步的,可以同步选择气的不同模式来实现。
AIO是异步的,即在进行I/O操作时,线程可以继续处理其他任务,而无需等待I/O操作的完成。
2.3.线程模型
BIO采用传统的一对一线程模型,即每个连接都需要一个独立的线程来处理。这回导致线程数量随着连接数增加而增加,容易造成资源的浪费。
NIO采用较为高效的多路复用器(Selector)机制,使用少量的线程来处理多个连接。
AIO采用基于事件驱动的线程模型,通过回调机制实现异步处理。
2.4.编程复杂度
BIO编程较为简单,但由于是阻塞式的,可能会导致性能瓶颈。
NIO编程较为复杂,需要了解选择器、通道和缓冲区等概念,但可以提供更好的性能和扩展性。
AIO编程相对更复杂,需要处理回调函数和处理器,但能够提供更高并发性和可伸缩性。
2.5.小结
选择何种I/O模型取决于具体应用程序的需求。如果应用程序的连接数较少且并发性要求不高,可以选择BIO。如果要求高并发性、较低的线程开销和更好的可扩展行,可以选择NIO。如果需要处理大量的并发连接,并希望通过异步操作来提高性能,可以选择AIO。
3.在java中的应用
当涉及到Java中的BIO、NIO和AIO时,我们可以使用Socket编程作为例子来解释它们的区别。
3.1.BIO
try (ServerSocket serverSocket = new ServerSocket(8080)) {
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
// 处理客户端请求,创建线程或线程池来处理每个连接
}
} catch (IOException e) {
e.printStackTrace();
}
在这个例子中,使用了阻塞式的'ServerSocket'和'Socket'。'accept()'方法会阻塞等待客户端的连接,直到客户端连接进来。在这期间,主线程会一直等待,直到有新的连接进来才会继续执行。
3.2.NIO
try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
serverSocketChannel.bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverChannel.accept(); // 非阻塞获取客户端连接
// 处理客户端请求,创建线程或线程池来处理每个连接
}
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
在这个例子中,使用了NIO的'ServerSocketChannel'和'SocketChannel'。首先,将'ServerSocketChannel'设置为非阻塞模式。然后,通过选择器('Selector')监听连接事件,当有连接到达时,通过非阻塞方式获取客户端连接。这样可以避免主线程一直等待,可以同时处理多个连接。
3.3.AIO
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
// 处理客户端请求,不需要阻塞等待连接
serverSocketChannel.accept(null, this);
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
});
Thread.sleep(Long.MAX_VALUE);
在这例子中,使用了AIO的'AsynchronousServerSocketChannel'和'AsynchronousSocketChannel'。通过异步方式监听连接事件,并在连接到达时调用回调方法'completed()'来处理客户端请求。这样,不需要阻塞等待连接,而是通过回调方式处理连接事件。
3.4.小结
这些例子展示了在Java中使用不同I/O模型的代码示例。BIO是阻塞的,需要一直等待连接;NIO是非阻塞式的,可以同时处理多个连接;AIO是异步的,通过回调方式处理连接事件,无需等待。