在网络编程的世界里,I/O操作是至关重要的组成部分,但传统的阻塞式I/O模型却常常成为性能瓶颈。想象一下,当你在一台服务器上处理数千个并发连接时,每个连接都需要一个独立的线程来处理读写操作。这不仅消耗了大量的系统资源,还导致了严重的上下文切换开销。于是,Java NIO(Non-blocking I/O)应运而生,为我们带来了非阻塞I/O的解决方案,开启了高并发处理的新纪元。
第二部分:非阻塞I/O的实践案例
实例1:基于NIO的简单聊天服务器
我们将构建一个简单的聊天服务器,使用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.HashSet;
import java.util.Set;
import java.util.Iterator;public class ChatServer {private static final int PORT = 8080;private static final ByteBuffer BUFFER_SIZE = ByteBuffer.allocate(1024);private static final Set<SocketChannel> clients = new HashSet<>();public static void main(String[] args) throws IOException {Selector selector = Selector.open();ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(PORT));serverChannel.configureBlocking(false);serverChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {selector.select();Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey key = it.next();if (key.isAcceptable()) {acceptNewClient(serverChannel, selector);}if (key.isReadable()) {readFromClient((SocketChannel) key.channel());}it.remove();}}}private static void acceptNewClient(ServerSocketChannel serverChannel, Selector selector) throws IOException {SocketChannel client = serverChannel.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);clients.add(client);}private static void readFromClient(SocketChannel client) throws IOException {BUFFER_SIZE.clear();int bytesRead = client.read(BUFFER_SIZE);if (bytesRead == -1) {clients.remove(client);client.close();} else {BUFFER_SIZE.flip();byte[] data = new byte[BUFFER_SIZE.limit()];BUFFER_SIZE.get(data);String message = new String(data).trim();System.out.println("Received: " + message);broadcastToAll(message, client);BUFFER_SIZE.clear();}}private static void broadcastToAll(String message, SocketChannel sender) throws IOException {for (SocketChannel client : clients) {if (client != sender) {ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());client.write(buffer);}}}
}
在这个例子中,我们首先创建了一个Selector
并打开了一个ServerSocketChannel
,将其绑定到一个端口上。然后,我们在一个无限循环中使用Selector
来监听新连接和已连接客户端的可读事件。当有新的客户端连接时,我们接受这个连接并将SocketChannel
注册到Selector
上,设置其为非阻塞模式。当SocketChannel
上有可读事件时,我们读取数据并广播给所有其他在线的客户端。
实例2:文件复制
下面是一个使用NIO进行文件复制的例子,展示如何使用FileChannel
和ByteBuffer
进行高效的数据传输。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.channels.FileChannel;public class FileCopier {public static void main(String[] args) {String sourcePath = "source.txt";String destPath = "destination.txt";try (FileChannel inChannel = FileChannel.open(Paths.get(sourcePath), StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get(destPath), StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW,StandardOpenOption.TRUNCATE_EXISTING)) {ByteBuffer buffer = ByteBuffer.allocate(1024);while (inChannel.read(buffer) > 0) {buffer.flip();outChannel.write(buffer);buffer.clear();}} catch (IOException e) {e.printStackTrace();}}
}
在上述代码中,我们首先打开源文件和目标文件的FileChannel
,然后使用一个ByteBuffer
作为中介来读取和写入数据。FileChannel
的read()
和write()
方法分别用于填充ByteBuffer
和清空它,从而实现了数据的高效传输。