BIO、NIO、AIO 都是 Java 中的 I/O 模型,下面讲述他们的主要区别,及其简单demo。
BIO 是 Java 最早的 I/O 模型,也称为同步阻塞 I/O。在 BIO 中,每个 I/O 操作都会阻塞当前线程,直到操作完成才会继续执行下一条语句,因此它的并发性较差。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BlockingIOServer {
public static void main(String[] args) {
try {
// 创建ServerSocket并绑定端口
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
// 监听客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("Accepted connection from client");
// 处理客户端请求
handleClientRequest(clientSocket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleClientRequest(Socket clientSocket) throws IOException {
InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
// 读取客户端发送的数据
while ((bytesRead = inputStream.read(buffer)) != -1) {
String request = new String(buffer, 0, bytesRead);
System.out.println("Received request: " + request);
// 处理请求并发送响应给客户端
String response = "This is the response from server";
outputStream.write(response.getBytes());
outputStream.flush();
}
// 关闭流和连接
inputStream.close();
outputStream.close();
clientSocket.close();
}
}
以上是一个使用阻塞I/O模型的简单服务器示例,它会监听8080端口上的客户端连接,并在接收到请求后发送响应。
请注意,该示例中的I/O操作(inputStream.read()
和 outputStream.write()
)都是阻塞的,也就是说当没有数据可读取或写入时,线程将被阻塞,直到有数据可用或写入完成。
这只是一个基本的示例,实际应用中可能需要考虑线程池、多线程处理和异常处理等方面的问题来提高性能和稳定性。
NIO 是 Java 1.4 引入的新 I/O 模型,也称为同步非阻塞 I/O。在 NIO 中,程序通过一个选择器(Selector)轮询注册的通道,如果某个通道有数据可读或可写,则进行相应的处理。由于 NIO 支持多路复用,因此单个线程可以同时管理多个通道,从而提高了并发性能。
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;
import java.util.Set;
public class NonBlockingIOServer {
public static void main(String[] args) {
try {
// 创建ServerSocketChannel并绑定端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 创建Selector并将Channel注册到Selector上
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 监听事件
selector.select();
// 处理就绪的事件
Set selectedKeys = selector.selectedKeys();
Iterator iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
// 处理新的连接请求
handleAcceptableEvent(serverSocketChannel, selector);
}
if (key.isReadable()) {
// 处理可读事件
handleReadableEvent(key);
}
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleAcceptableEvent(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("Accepted connection from client");
}
private static void handleReadableEvent(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
// 连接关闭
clientChannel.close();
return;
}
if (bytesRead > 0) {
buffer.flip();
byte[] requestData = new byte[buffer.remaining()];
buffer.get(requestData);
String request = new String(requestData);
System.out.println("Received request: " + request);
// 处理请求
String response = "This is the response from server";
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
clientChannel.write(responseBuffer);
}
}
}
以上是一个使用非阻塞I/O模型的简单服务器示例,它会监听8080端口上的客户端连接,并在接收到请求后发送响应。
这个示例中使用了ServerSocketChannel
来接受新的连接,并将其配置为非阻塞模式。然后将ServerSocketChannel
注册到Selector
上,并指定关注SelectionKey.OP_ACCEPT
事件。
在事件循环中,先调用selector.select()
来监听事件,然后使用迭代器处理就绪的事件。对于OP_ACCEPT
事件,调用handleAcceptableEvent()
方法处理新连接请求,并将新的SocketChannel
注册到Selector
上,并指定关注SelectionKey.OP_READ
事件。对于OP_READ
事件,调用handleReadableEvent()
方法读取客户端发送的数据并处理请求。
需要注意的是,NIO模型下的非阻塞I/O需要处理更多的细节,如缓冲区的管理、事件选择和合适的线程池等。这里只提供了一个简单的示例,实际应用中可能需要根据需求做出相应的调整和优化。
AIO 是 Java 1.7 引入的新 I/O 模型,也称为异步非阻塞 I/O。在 AIO 中,当进行 I/O 操作时,操作系统会立即返回,并不会阻塞当前线程。当操作完成后,操作系统会通知应用程序,然后再由应用程序对数据进行处理。因此,AIO 可以有效地利用 CPU 时间,提高并发性能。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Executors;
public class AsyncIOServer {
private static final int PORT = 8080;
public static void main(String[] args) throws IOException {
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(10));
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.accept(null, new CompletionHandler() {
@Override
public void completed(AsynchronousSocketChannel clientChannel, Void attachment) {
// 处理新的连接
serverSocketChannel.accept(null, this);
handleClientChannel(clientChannel);
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
});
}
private static void handleClientChannel(AsynchronousSocketChannel clientChannel) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
clientChannel.read(buffer, null, new CompletionHandler() {
@Override
public void completed(Integer bytesRead, Void attachment) {
if (bytesRead == -1) {
try {
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
if (bytesRead > 0) {
buffer.flip();
byte[] requestData = new byte[buffer.remaining()];
buffer.get(requestData);
String request = new String(requestData);
System.out.println("Received request: " + request);
// 处理请求
String response = "This is the response from server";
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
clientChannel.write(responseBuffer, null, new CompletionHandler() {
@Override
public void completed(Integer bytesWritten, Void attachment) {
if (responseBuffer.hasRemaining()) {
clientChannel.write(responseBuffer, null, this);
} else {
try {
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
});
}
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
});
}
}
以上是一个使用异步I/O模型的简单服务器示例,它会监听8080端口上的客户端连接,并在接收到请求后发送响应。
这个示例中使用了AsynchronousServerSocketChannel
来接受新的连接,并将其配置为异步模式。然后,调用accept()
方法来接收新的连接,并将CompletionHandler
实例作为参数传递,并在回调函数中处理新连接请求。对于已经建立的连接,使用read()
方法读取客户端发送的数据,并将CompletionHandler
实例作为参数传递,在回调函数中处理请求。
需要注意的是,异步I/O模型下的编程比较复杂,需要熟练掌握CompletionHandler
的使用,以及如何管理缓冲区和事件选择等。这里只提供了一个简单的示例,实际应用中可能需要根据需求做出相应的调整和优化。
综上所述,BIO、NIO、AIO 都是 Java 中的 I/O 模型,它们的主要区别在于处理 I/O 操作的方式不同。BIO 是同步阻塞模型,每个 I/O 操作都会阻塞当前线程;NIO 是同步非阻塞模型,通过选择器轮询注册的通道实现多路复用,提高了并发性能;AIO 是异步非阻塞模型,操作系统立即返回并不会阻塞当前线程,可以有效地利用 CPU 时间,进一步提高并发性能。因此,根据实际应用场景和需求,选择合适的 I/O 模型非常重要。
更多消息资讯,请访问昂焱数据(https://www.ayshuju.com/home)