Java NIO详解:非阻塞IO与缓冲区
Java NIO详解:非阻塞IO与缓冲区
概述
Java NIO(New IO)是JDK 1.4引入的非阻塞IO API。NIO提供了与标准IO不同的工作方式,支持面向缓冲区的、基于通道的IO操作,以及选择器机制。
1. 缓冲区(Buffer)
基本缓冲区操作
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
public class BufferExample {
public static void main(String[] args) {
// 创建IntBuffer
IntBuffer intBuffer = IntBuffer.allocate(10);
// 写入数据
for (int i = 0; i < 5; i++) {
intBuffer.put(i * 10);
}
System.out.println("容量: " + intBuffer.capacity());
System.out.println("限制: " + intBuffer.limit());
System.out.println("位置: " + intBuffer.position());
// 切换到读模式
intBuffer.flip();
System.out.println("切换后位置: " + intBuffer.position());
System.out.println("切换后限制: " + intBuffer.limit());
// 读取数据
while (intBuffer.hasRemaining()) {
System.out.print(intBuffer.get() + " ");
}
System.out.println();
// 清空缓冲区
intBuffer.clear();
System.out.println("清空后位置: " + intBuffer.position());
System.out.println("清空后限制: " + intBuffer.limit());
}
}
ByteBuffer操作
import java.nio.ByteBuffer;
public class ByteBufferExample {
public static void main(String[] args) {
// 创建ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(100);
// 写入数据
String text = "Hello, NIO!";
buffer.put(text.getBytes());
System.out.println("写入后位置: " + buffer.position());
// 切换到读模式
buffer.flip();
// 读取数据
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String result = new String(bytes);
System.out.println("读取: " + result);
// 直接缓冲区(堆外内存)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(100);
directBuffer.put("Direct Buffer".getBytes());
directBuffer.flip();
byte[] directBytes = new byte[directBuffer.remaining()];
directBuffer.get(directBytes);
System.out.println("直接缓冲区: " + new String(directBytes));
}
}
2. 通道(Channel)
文件通道
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.nio.ByteBuffer;
public class FileChannelExample {
public static void main(String[] args) {
try {
// 写入文件
FileChannel writeChannel = FileChannel.open(
Paths.get("nio-write.txt"),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE
);
ByteBuffer buffer = ByteBuffer.wrap("Hello, NIO Channel!".getBytes());
writeChannel.write(buffer);
writeChannel.close();
System.out.println("文件已写入");
// 读取文件
FileChannel readChannel = FileChannel.open(
Paths.get("nio-write.txt"),
StandardOpenOption.READ
);
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
readChannel.read(readBuffer);
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
System.out.println("读取: " + new String(bytes));
readChannel.close();
// 文件复制
FileChannel sourceChannel = FileChannel.open(
Paths.get("nio-write.txt"),
StandardOpenOption.READ
);
FileChannel destChannel = FileChannel.open(
Paths.get("nio-copy.txt"),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE
);
destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
sourceChannel.close();
destChannel.close();
System.out.println("文件已复制");
} catch (Exception e) {
System.out.println("异常: " + e.getMessage());
}
}
}
3. 选择器(Selector)
import java.nio.channels.*;
import java.nio.*;
import java.util.*;
public class SelectorExample {
public static void main(String[] args) {
try {
// 创建选择器
Selector selector = Selector.open();
// 创建服务器套接字通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new java.net.InetSocketAddress(8080));
// 注册选择器
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动,等待连接...");
// 选择就绪的通道
while (true) {
int readyChannels = selector.select();
if (readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// 处理接受连接
System.out.println("接受连接");
} else if (key.isReadable()) {
// 处理读取
System.out.println("读取数据");
} else if (key.isWritable()) {
// 处理写入
System.out.println("写入数据");
}
keyIterator.remove();
}
}
} catch (Exception e) {
System.out.println("异常: " + e.getMessage());
}
}
}
4. 文件NIO操作
import java.nio.file.*;
import java.nio.*;
import java.util.*;
import java.util.stream.*;
public class FileNIOExample {
public static void main(String[] args) {
try {
// 读取文件
List<String> lines = Files.readAllLines(Paths.get("test.txt"));
System.out.println("文件内容:");
lines.forEach(System.out::println);
// 写入文件
Files.write(Paths.get("nio-output.txt"),
Arrays.asList("Line 1", "Line 2", "Line 3"));
System.out.println("文件已写入");
// 使用Stream读取文件
try (Stream<String> stream = Files.lines(Paths.get("nio-output.txt"))) {
stream.forEach(System.out::println);
}
// 目录操作
Path dir = Paths.get("nio-dir");
if (!Files.exists(dir)) {
Files.createDirectory(dir);
}
// 创建临时文件
Path tempFile = Files.createTempFile(dir, "temp", ".txt");
Files.write(tempFile, "临时文件内容".getBytes());
System.out.println("临时文件: " + tempFile);
// 遍历目录
try (Stream<Path> paths = Files.walk(dir)) {
paths.filter(Files::isRegularFile)
.forEach(System.out::println);
}
// 查找文件
try (Stream<Path> paths = Files.find(dir, 5,
(path, attrs) -> path.toString().endsWith(".txt"))) {
paths.forEach(System.out::println);
}
} catch (Exception e) {
System.out.println("异常: " + e.getMessage());
}
}
}
5. 内存映射文件
import java.nio.channels.FileChannel;
import java.nio.MappedByteBuffer;
import java.nio.file.*;
import java.io.*;
public class MemoryMappedFileExample {
public static void main(String[] args) {
try {
// 写入测试文件
Files.write(Paths.get("mapped.txt"),
"Hello, Memory Mapped File!".getBytes());
// 内存映射文件读取
FileChannel channel = FileChannel.open(
Paths.get("mapped.txt"),
StandardOpenOption.READ
);
MappedByteBuffer mappedBuffer = channel.map(
FileChannel.MapMode.READ_ONLY,
0,
channel.size()
);
byte[] bytes = new byte[mappedBuffer.remaining()];
mappedBuffer.get(bytes);
System.out.println("内存映射读取: " + new String(bytes));
channel.close();
} catch (Exception e) {
System.out.println("异常: " + e.getMessage());
}
}
}
6. 实际应用示例
大文件处理
import java.nio.file.*;
import java.nio.channels.*;
import java.nio.*;
import java.util.stream.*;
public class LargeFileProcessor {
public static void processLargeFile(String filePath) throws Exception {
FileChannel channel = FileChannel.open(
Paths.get(filePath),
StandardOpenOption.READ
);
long size = channel.size();
long position = 0;
int chunkSize = 1024 * 1024; // 1MB
while (position < size) {
long bytesToRead = Math.min(chunkSize, size - position);
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_ONLY,
position,
bytesToRead
);
// 处理数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("处理数据块: " + position + " - " + (position + bytesToRead));
position += bytesToRead;
}
channel.close();
}
public static void main(String[] args) {
try {
// 创建测试文件
Files.write(Paths.get("large.txt"),
"Large file content".repeat(1000).getBytes());
processLargeFile("large.txt");
} catch (Exception e) {
System.out.println("异常: " + e.getMessage());
}
}
}
7. 最佳实践
- 使用NIO进行大文件操作:内存映射文件适合处理大文件
- 使用选择器处理网络IO:非阻塞IO适合高并发场景
- 使用Files工具类:简化文件操作
- 正确管理缓冲区:注意flip()、clear()等方法的使用
- 处理编码问题:使用正确的字符编码
总结
Java NIO提供了非阻塞IO和内存映射文件等高级功能,适合处理大文件和高并发网络应用。掌握NIO的核心概念和使用方法,是编写高性能Java程序的重要技能。