在Java中,输入输出(I/O)操作是非常核心的功能之一。Java提供了多种I/O模型,主要包括:字节流和字符流、阻塞I/O(BIO)、非阻塞I/O(NIO)、异步I/O(AIO)、以及直接内存访问(Direct Buffer)。下面将对这五种I/O模型进行详细阐述。
1. 字节流与字符流
Java中的I/O流分为字节流(InputStream和OutputStream)和字符流(Reader和Writer)。字节流处理的是原始的二进制数据,适合所有类型的I/O操作;而字符流则是专门用于处理文本数据,提供了对Unicode字符的支持。
// 字节流示例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
int byteData;
while ((byteData = fis.read()) != -1) {
fos.write(byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 阻塞I/O(BIO)
阻塞I/O是Java最传统的I/O处理方式。在这种模型中,当进行I/O操作时,线程会被阻塞直到操作完成。如果涉及到网络通信,一个线程会在尝试读取数据时被堵塞,直到数据可用或者连接关闭。
// BIO示例
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class BioServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(1234);
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(new ClientHandler(clientSocket)).start();
}
}
}
class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println("Echo: " + inputLine);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 非阻塞I/O(NIO)
非阻塞I/O使用了Java NIO包,它允许一个线程处理多个I/O操作。NIO引入了选择器(Selector),使得线程在等待I/O事件时不会被阻塞,从而提高了应用的并发性能。
// NIO示例
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels SelectionKey;
public class NioServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(1234));
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) {
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
int bytesRead = client.read(buffer);
if (bytesRead == -1) {
client.close();
} else {
buffer.flip();
client.write(buffer);
}
}
selector.selectedKeys().remove(key);
}
}
}
}
4. 异步I/O(AIO)
Java AIO(Java 7引入)允许应用程序在执行I/O操作时可以继续处理其他任务。当I/O操作完成后,系统会通知相应的回调,避免了阻塞。
// AIO示例
import java.io.IOException;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.ByteBuffer;
public class AioServer {
public static void main(String[] args) throws IOException {
AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(1234));
serverSocket.accept(null, new AcceptHandler(serverSocket));
}
}
class AcceptHandler implements java.nio.channels.CompletionHandler<AsynchronousSocketChannel, Void> {
private final AsynchronousServerSocketChannel server;
public AcceptHandler(AsynchronousServerSocketChannel server) {
this.server = server;
}
@Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
server.accept(null, this);
ByteBuffer buffer = ByteBuffer.allocate(1024);
result.read(buffer, buffer, new ReadHandler(result));
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
}
}
// ReadHandler类省略,类似于AcceptHandler,负责读取数据
5. 直接内存访问(Direct Buffer)
在Java中,直接内存缓冲区可以提高I/O操作的性能。它允许Java直接在堆外内存中分配缓冲区,从而减少了数据拷贝的次数。
// 直接内存缓存示例
import java.nio.ByteBuffer;
public class DirectBufferExample {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// 使用缓冲区
buffer.put("Hello, Direct Buffer!".getBytes());
buffer.flip();
// 读取缓冲区
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
}
}
总结
Java提供了丰富的I/O模型,以适应不同的应用需求。通过字节流和字符流,我们可以读取和写入数据;使用BIO,我们可以处理简单的阻塞I/O操作;而NIO和AIO则为高并发提供了更好的解决方案;直接内存访问则通过降低内存拷贝的开销来提升性能。熟练掌握这些I/O模型,能够帮助开发者在构建高效的Java应用时游刃有余。