各种 IO 🏳️🌈
一段感情最终都要有一个结局,除了深厚的爱以外,还要有无限的坚持及对这份感情始终如一的信任。这样才能克服在相爱过程中的种种困难和折磨
IO模型中同步与异步, 阻塞与非阻塞是什么?
在IO模型中,同步与异步以及阻塞与非阻塞这些概念是用来描述数据传输过程中的通信方式的。下面我将分别解释这些概念,并说明它们之间的差别与联系。
- 同步与异步:
同步(Synchronous):在同步通信中,发送方会等待接收方处理完数据后再进行下一步操作。这就意味着发送方在等待期间可能无法进行其他任务,因为它需要等待接收方的响应。
异步(Asynchronous):与同步相反,在异步通信中,发送方不会等待接收方处理完数据。发送方发送数据后,可以立即进行其他任务,而不需要等待接收方的响应。接收方在处理完数据后,会通过某种方式通知发送方。
- 阻塞与非阻塞:
阻塞(Blocking):在阻塞通信中,发送方发送数据后,会等待接收方接收并处理数据,期间发送方无法进行其他操作。换句话说,发送方被“阻塞”在那里,直到接收到接收方的响应。
非阻塞(Non-blocking):与阻塞相反,在非阻塞通信中,发送方在发送数据后不会等待接收方的响应,而是继续进行其他操作。当接收方处理完数据后,会通过某种方式通知发送方。
- 差别与联系:
同步通信强调的是发送方与接收方之间的通信顺序,即发送方需要等待接收方处理完数据后再进行下一步操作。而异步通信允许发送方在发送数据后继续进行其他操作,不需要等待接收方的响应。
阻塞通信与非阻塞通信的差别在于发送方是否等待接收方的响应。阻塞通信中,发送方会被“阻塞”在那里,直到接收到接收方的响应;非阻塞通信则允许发送方继续进行其他操作。
同步与异步以及阻塞与非阻塞之间存在联系。例如,同步通信往往是阻塞的,因为发送方需要等待接收方的响应。异步通信往往是非阻塞的,因为发送方不需要等待接收方的响应。但是,也有同步非阻塞和异步阻塞的情况,这取决于具体实现。
总之,在IO模型中,同步与异步主要描述的是发送方与接收方之间的通信顺序,而阻塞与非阻塞主要描述的是发送方在等待接收方响应时是否可以进行其他操作。
通过java中socket,nio, aio解释上面的同步异步,阻塞非阻塞
Java中的Socket、NIO和AIO分别代表了不同的I/O处理方式,它们分别实现了上述同步异步、阻塞非阻塞的不同组合。以下是它们的具体描述:
- Socket(同步阻塞IO):
Java中的传统Socket通信(也称为BIO,Blocking IO)采用同步阻塞方式进行数据传输。在这种模式下,服务器端为每个客户端连接创建一个独立的线程,负责处理该客户端的所有请求。当一个线程在等待客户端发送数据时,它会被阻塞,直到收到数据或发生超时。这种模式下,服务器端需要为每个连接创建大量线程,可能导致线程资源耗尽。 - NIO(同步非阻塞IO):
Java NIO(New IO)引入了Channel和Selector的概念,实现了同步非阻塞的IO处理。在NIO中,服务器端使用一个线程(或线程池)处理多个客户端连接。这些连接通过Channel注册到Selector上,当有数据传输时,Selector会通知服务器端线程。服务器线程可以非阻塞地处理多个客户端的请求。这种方式相较于传统Socket通信,大大提高了服务器的并发处理能力。 - AIO(异步非阻塞IO):
Java AIO(Asynchronous IO,也称为NIO.2)引入了异步非阻塞的IO处理方式。AIO中,服务器端可以在数据传输时不阻塞地处理其他任务。当数据传输完成,服务器会收到一个通知。这是通过CompletionHandler接口实现的,当操作完成时,CompletionHandler的回调方法会被执行。这种方式进一步提高了服务器端的并发处理能力。
总结:
- Socket(BIO):同步阻塞,每个客户端连接需要一个独立的线程处理,可能导致线程资源耗尽。
- NIO:同步非阻塞,通过Channel和Selector实现,一个线程(或线程池)可以处理多个客户端连接,提高了服务器的并发处理能力。
- AIO(NIO.2):异步非阻塞,服务器端在数据传输时可以非阻塞地处理其他任务,当数据传输完成时,通过CompletionHandler回调方法通知服务器,进一步提高了并发处理能力。
Socket(BIO)、NIO和AIO实现简单的服务器端和客户端通信的代码示例
下面是使用Socket(BIO)、NIO和AIO实现简单的服务器端和客户端通信的代码示例,以及同步异步、阻塞非阻塞的过程解释。
- Socket(BIO,同步阻塞):
服务器端代码示例:
import java.io.*;
import java.net.*;
public class SocketServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = reader.readLine(); // 阻塞等待客户端数据
System.out.println("Received: " + message);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start(); // 为每个客户端连接创建一个新线程处理
}
}
}
客户端代码示例:
import java.io.*;
import java.net.*;
public class SocketClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 8080);
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println("Hello, server!"); // 同步发送数据
writer.flush();
socket.close();
}
}
在Socket(BIO)中,服务器端的accept()
方法和客户端的readLine()
方法都是同步阻塞的。服务器端需要为每个客户端连接创建一个新线程处理,可能导致线程资源耗尽。
2. NIO(同步非阻塞):
服务器端代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
public class NIOServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // 阻塞等待就绪Channel
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.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(1024);
int bytesRead = client.read(buffer);
if (bytesRead > 0) {
buffer.flip();
System.out.println("Received: " + new String(buffer.array(), 0, bytesRead));
client.close();
}
}
}
}
}
}
NIO(同步非阻塞):
客户端代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NIOClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost", 8080));
while (!socketChannel.finishConnect()) {
// 在连接完成之前,可以做其他的事情
System.out.println("Waiting for connection...");
}
ByteBuffer buffer = ByteBuffer.wrap("Hello, server!".getBytes());
while (buffer.hasRemaining()) {
socketChannel.write(buffer); // 非阻塞写
}
socketChannel.close();
}
}
这个NIO客户端示例展示了如何使用SocketChannel
建立非阻塞连接并发送数据。configureBlocking(false)
使SocketChannel
工作在非阻塞模式。finishConnect()
方法用于检查连接是否完成,如果连接还未完成,程序可以在此期间处理其他任务。
在前面的回答中,我已经给出了Socket(BIO)和NIO的服务器端和客户端代码示例。现在我将继续给出AIO(异步非阻塞)的服务器端和客户端代码示例。
3. AIO(异步非阻塞):
服务器端代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;
public class AIOServer {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel client, Object attachment) {
serverChannel.accept(null, this); // 接受下一个连接
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> readResult = client.read(buffer);
try {
readResult.get(); // 等待读操作完成
buffer.flip();
System.out.println("Received: " + new String(buffer.array(), 0, buffer.limit()));
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("Failed to accept a connection.");
exc.printStackTrace();
}
});
// 由于是异步非阻塞模式,为了保持服务器运行,这里使用Thread.sleep
while (true) {
Thread.sleep(1000);
}
}
}
客户端代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future;
public class AIOClient {
public static void main(String[] args) throws IOException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
Future<Void> connectResult = socketChannel.connect(new InetSocketAddress("localhost", 8080));
try {
connectResult.get(); // 等待连接完成
ByteBuffer buffer = ByteBuffer.wrap("Hello, server!".getBytes());
Future<Integer> writeResult = socketChannel.write(buffer);
writeResult.get(); // 等待写操作完成
socketChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在AIO(异步非阻塞)中,服务器端通过AsynchronousServerSocketChannel
和CompletionHandler
处理连接和读取数据的回调。当服务器接收到客户端的连接请求或数据时,会异步地执行相应的回调方法,这使得服务器能够在等待数据传输时处理其他任务。客户端通过AsynchronousSocketChannel
连接服务器并发送数据,同样采用异步非阻塞方式。
综上,Socket(BIO)使用同步阻塞方式处理数据传输,每个客户端连接需要一个独立的线程处理;NIO使用同步非阻塞方式处理数据传输,一个线程(或线程池)可以处理多个客
总结:
- Socket(BIO):同步阻塞,每个客户端连接需要一个独立的线程处理,可能导致线程资源耗尽。
- NIO:同步非阻塞,通过Channel和Selector实现,一个线程(或线程池)可以处理多个客户端连接,提高了服务器的并发处理能力。客户端可以在等待连接时处理其他任务。
- AIO(NIO.2):异步非阻塞,服务器端在数据传输时可以非阻塞地处理其他任务,当数据传输完成时,通过CompletionHandler回调方法通知服务器,进一步提高了并发处理能力。客户端可以在等待连接和数据传输时处理其他任务。