07-BIO和NIO
简洁版
BIO:同步阻塞模型,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。应用场景:中小规模的Web应用。
NIO:(Non-blocking I/O,在Java领域,也称为New I/O,是相对于BIO来说的)是一种同步非阻塞的I/O模型,也是I/O多路复用的基础。它通过使用单个或少量线程来处理多个I/O操作,避免了传统的每个请求一个线程的做法。应用场景:聊天服务器。
Java中的java.nio
包及其下的Channel
、Selector
、Buffer
等类就是基于NIO模型的实现。
- Selector(选择器): 用于监控多个通道的 I/O 事件,实现高效的多路复用。
- Channel(通道):用于高效的 I/O 操作,支持非阻塞模式和双向操作。如:FileChannel用于文件 I/O 操作。
- Buffer(缓冲区):用于与通道交互的容器,管理数据的读写操作。如:ByteBuffer用于字节数据。
Selector对应一个线程,一个线程对应多个Channel连接,Channel注册到selector。每个Channel 都会对应一个Buffer,

补充:
多路复用是一种高效的 I/O 管理技术,允许一个线程监控多个通道的 I/O 事件。多路复用通常用于高效地管理多个 I/O 操作,特别是在处理大量并发连接时。在 Java NIO 中,多路复用主要通过
Selector
类来实现。Selector
允许一个单独的线程监控多个通道(Channel)的 I/O 事件(如读、写、连接等),从而提高了 I/O 操作的效率。应用场景:聊天服务器、游戏服务器等。
GPT
BIO(Blocking I/O)
- 同步阻塞:BIO是一种同步阻塞模型。每个连接请求都会创建一个线程,线程会阻塞,直到I/O操作完成。
- 简单易用:BIO模型简单,适用于连接数目较少且并发性不高的应用场景,如传统的Web应用。
- 资源消耗大:因为每个请求都需要一个独立的线程进行处理,所以在高并发场景下,线程数目会迅速增长,导致系统资源消耗大,并且线程上下文切换的开销也较大。
- 典型实现:Java中的
ServerSocket
和Socket
类就是基于BIO模型的实现。
BIO应用领域
传统Web服务器:
- 中小规模的Web应用:处理并发请求数量较少的Web服务器,如一些企业内部应用或中小型网站。
- 简单的HTTP服务器:需要快速开发和部署的小型HTTP服务器。
客户端应用:
- 桌面应用:一些简单的桌面客户端程序需要与服务器进行通信,可以使用BIO来实现。
- 命令行工具:一些需要网络通信的命令行工具,如FTP客户端等。
数据库连接:
- 传统数据库客户端:连接到数据库服务器并执行查询操作的客户端程序,适用于连接数较少的场景。
教育和培训:
- 学习和教学:由于BIO模型简单,适合初学者学习和理解网络编程的基本概念。
NIO(New I/O)
- 非阻塞:NIO是一种非阻塞模型。它通过使用单个或少量线程来处理多个I/O操作,避免了传统的每个请求一个线程的做法。
- 多路复用:NIO使用选择器(Selector)进行多路复用,可以在单个线程中监控多个通道(Channel)的事件,比如读、写和连接。
- 更高的并发性:由于NIO不需要为每个连接创建一个线程,因此它在处理大量并发连接时更加高效,适用于高并发、高负载的网络应用,如高性能服务器。
- 复杂性高:NIO的编程模型比BIO复杂,需要更复杂的代码来处理选择器和通道的非阻塞操作。
- 典型实现:Java中的
java.nio
包及其下的Channel
、Selector
、Buffer
等类就是基于NIO模型的实现。
NIO应用领域
高性能服务器:
- 高并发Web服务器:处理大量并发请求的高性能Web服务器,如电商平台、内容分发网络(CDN)等。
- 游戏服务器:需要处理大量并发连接的在线游戏服务器。
分布式系统:
- 消息队列:如Kafka等高性能消息队列,使用NIO来处理高吞吐量的数据传输。
- 大数据处理:大数据系统如Hadoop、Spark等,使用NIO来高效地处理数据传输。
网络框架:
- 异步网络框架:如Netty,使用NIO来实现高效的网络通信,广泛应用于各种高性能网络应用中。
- HTTP客户端和服务器:高性能HTTP客户端和服务器框架,如AsyncHttpClient等。
文件处理:
- 大文件传输:处理大文件传输和操作的系统,如分布式文件系统(HDFS)等。
实时通信:
- 聊天系统:即时通讯工具和在线聊天系统,需要处理大量并发连接和实时数据传输。
- 在线会议:视频会议和音频通话应用,需要高效的网络通信和数据传输。
对比总结
- 编程模型:BIO编程简单,但不适合高并发。NIO编程复杂,但在高并发情况下性能更好。
- 资源消耗:BIO需要更多的线程和系统资源。NIO通过少量线程管理多个连接,资源利用效率更高。
- 适用场景:BIO适用于简单、连接数少的场景。NIO适用于高并发、高负载的场景。
- BIO:适用于简单、连接数较少、并发性要求不高的应用场景。开发和理解相对简单,适合初学者和小型项目。
- NIO:适用于高并发、高性能需求的应用场景。能够高效地处理大量并发连接,适合大型分布式系统和高性能服务器。
简书
https://www.jianshu.com/p/8ad464ed516e
各种I/O对比

属性\模型 | 阻塞BIO | 非阻塞NIO | 异步AIO |
---|---|---|---|
blocking | 阻塞并同步 | 非阻塞但同步 | 非阻塞并异步 |
线程数(server:client) | 1:1 | 1:N | 0:N |
复杂度 | 简单 | 较复杂 | 复杂 |
吞吐量 | 低 | 高 | 高 |
具体使用得依据业务的实际应用场景和性能需求而定,如果客户端很少,并发量不大,那么完全可以选择BIO,不过得加入线程池管理;相反要求并发较高的话,就应该采用NIO框架了。
BIO、NIO、AIO概念认知
- Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
- Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
- Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
BIO、NIO、AIO适用场景分析
- BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
- NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
- AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
Channel、Selector、Buffer
java.nio
(New I/O)包是 Java 1.4 引入的一个新的 I/O API,旨在提供更高效的 I/O 操作。它主要通过非阻塞 I/O 和基于通道(Channel)和缓冲区(Buffer)的 I/O 操作来实现这一点。以下是 java.nio
包中一些关键类的主要作用:
1. Channel
Channel
是一个新的 I/O 抽象,类似于传统的流(Stream),但它更强大。通道可以读写数据,并且可以是非阻塞的。
主要类型:
- FileChannel:用于文件 I/O 操作。
- SocketChannel:用于网络 I/O 操作(TCP)。
- ServerSocketChannel:用于监听 TCP 连接。
- DatagramChannel:用于网络 I/O 操作(UDP)。
主要特点:
- 双向性:通道可以同时进行读和写操作。
- 非阻塞模式:通道可以配置为非阻塞模式,允许异步 I/O 操作。
- 与缓冲区结合使用:通道与缓冲区结合使用,数据从通道读入缓冲区或从缓冲区写入通道。
示例代码:
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelExample {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("example.txt", "rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
bytesRead = channel.read(buffer);
}
channel.close();
file.close();
}
}
2. Selector
Selector
是一个多路复用器,用于监控多个通道的 I/O 事件(如读、写、连接等)。它允许一个单独的线程管理多个通道,从而提高了 I/O 操作的效率。
主要特点:
- 非阻塞 I/O:与非阻塞通道一起使用,允许单个线程处理多个通道的 I/O 事件。
- 事件驱动:通过注册感兴趣的事件(如
OP_READ
、OP_WRITE
、OP_CONNECT
、OP_ACCEPT
),Selector 可以在事件发生时通知应用程序。
示例代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class SelectorExample {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress("localhost", 8080));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer);
System.out.println("Received: " + new String(buffer.array()).trim());
}
}
}
}
}
3. Buffer
Buffer
是一个用于与 NIO 通道进行交互的容器。它是一个线性、有限的字节序列,具有固定的容量(capacity)、位置(position)和限制(limit)。
主要类型:
- ByteBuffer:用于字节数据。
- CharBuffer:用于字符数据。
- IntBuffer:用于整数数据。
- FloatBuffer:用于浮点数数据。
- DoubleBuffer:用于双精度浮点数数据。
- LongBuffer:用于长整数数据。
- ShortBuffer:用于短整数数据。
主要特点:
- 容量(Capacity):缓冲区的最大存储量。
- 位置(Position):下一个要读或写的元素的索引。
- 限制(Limit):缓冲区中第一个不能读或写的元素的索引。
示例代码:
import java.nio.ByteBuffer;
public class BufferExample {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(10);
for (int i = 0; i < buffer.capacity(); i++) {
buffer.put((byte) i);
}
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print(buffer.get() + " ");
}
}
}
总结
- Channel:用于高效的 I/O 操作,支持非阻塞模式和双向操作。
- Selector:用于监控多个通道的 I/O 事件,实现高效的多路复用。
- Buffer:用于与通道交互的容器,管理数据的读写操作。
通过结合使用这些类,Java NIO 提供了比传统 I/O 更高效和灵活的 I/O 操作方式。