本文目录导读:

我来通过几个典型的案例来说明Java IO等待的情况:
传统阻塞IO(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(8080);
System.out.println("服务器启动,等待客户端连接...");
while (true) {
// 这里会阻塞,等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("客户端连接:" + socket.getRemoteSocketAddress());
// 为每个客户端创建一个线程处理
new Thread(() -> {
try {
handleClient(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
private static void handleClient(Socket socket) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
String line;
while ((line = in.readLine()) != null) {
System.out.println("收到消息:" + line);
out.println("服务器响应:" + line.toUpperCase());
}
}
}
阻塞文件读取
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BlockingFileRead {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
try (InputStream is = new FileInputStream("large_file.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
System.out.println("开始读取文件...");
while ((bytesRead = is.read(buffer)) != -1) {
// 模拟处理数据
new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
// 每个循环打印进度
System.out.println("读取了 " + bytesRead + " 字节");
}
} catch (IOException e) {
e.printStackTrace();
}
long duration = System.currentTimeMillis() - startTime;
System.out.println("文件读取完成,耗时:" + duration + "ms");
}
}
等待日志文件写入(模拟等待场景)
import java.io.*;
import java.util.Random;
import java.util.concurrent.*;
public class IOWaitingDemo {
private static final BlockingQueue<String> logQueue = new LinkedBlockingQueue<>();
public static void main(String[] args) throws Exception {
// 创建多个任务同时写入日志
ExecutorService executor = Executors.newFixedThreadPool(3);
System.out.println("开始并发写入日志...");
long startTime = System.currentTimeMillis();
// 创建3个写日志任务
for (int i = 0; i < 3; i++) {
final int taskId = i;
executor.submit(() -> {
try {
writeLog("日志任务-" + taskId);
} catch (InterruptedException | IOException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
long duration = System.currentTimeMillis() - startTime;
System.out.println("所有日志写入完成,总耗时:" + duration + "ms");
}
private static void writeLog(String taskName) throws InterruptedException, IOException {
File logFile = new File("logs_" + taskName + ".log");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(logFile))) {
Random random = new Random();
for (int i = 0; i < 10; i++) {
// 模拟IO等待:随机延迟
Thread.sleep(random.nextInt(500));
writer.write("[" + taskName + "] 日志第" + (i+1) + "行: " + System.currentTimeMillis());
writer.newLine();
System.out.println(taskName + " 写入第 " + (i+1) + " 行日志");
}
}
}
}
NIO非阻塞与等待对比
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
public class NIOVsBIODemo {
public static void main(String[] args) throws IOException {
// NIO 非阻塞服务器
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 非阻塞模式
serverChannel.bind(new InetSocketAddress(9090));
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO服务器启动,等待连接...");
while (true) {
// 非阻塞地等待事件
int readyChannels = selector.select(100); // 最多等待100ms
if (readyChannels == 0) {
System.out.println("当前无连接请求,可以执行其他任务...");
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
if (key.isAcceptable()) {
// 接受连接
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("接受新连接:" + clientChannel.getRemoteAddress());
} else if (key.isReadable()) {
// 读取数据
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
clientChannel.close();
key.cancel();
} else if (bytesRead > 0) {
String message = new String(buffer.array(), 0, bytesRead);
System.out.println("收到消息:" + message);
// 响应客户端
ByteBuffer response = ByteBuffer.wrap(("已收到: " + message).getBytes());
clientChannel.write(response);
}
}
}
keys.clear();
}
}
}
模拟数据库查询等待
import java.util.concurrent.*;
import java.util.*;
public class DatabaseWaitingSimulation {
private static final Map<String, Connection> connections = new ConcurrentHashMap<>();
static class Connection {
private boolean inUse = false;
private int id;
Connection(int id) {
this.id = id;
}
synchronized boolean acquire() {
if (!inUse) {
inUse = true;
return true;
}
return false;
}
synchronized void release() {
inUse = false;
}
@Override
public String toString() {
return "Connection-" + id;
}
}
public static void main(String[] args) throws Exception {
// 初始化连接池
for (int i = 1; i <= 3; i++) {
connections.put("conn-" + i, new Connection(i));
}
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<String>> results = new ArrayList<>();
System.out.println("模拟数据库并发查询...");
// 创建10个并发查询任务
for (int i = 0; i < 10; i++) {
final int queryId = i;
Future<String> future = executor.submit(() -> {
return executeQuery(queryId);
});
results.add(future);
}
// 收集结果
for (Future<String> future : results) {
System.out.println(future.get());
}
executor.shutdown();
}
private static String executeQuery(int queryId) throws InterruptedException {
System.out.println("查询 " + queryId + " 开始,等待连接池分配连接...");
// 等待空闲连接
Connection connection = null;
long startWait = System.currentTimeMillis();
while (connection == null) {
// 尝试获取连接
for (Connection conn : connections.values()) {
if (conn.acquire()) {
connection = conn;
break;
}
}
if (connection == null) {
// 没有空闲连接,等待100ms后重试
Thread.sleep(100);
// 等待超时检查
if (System.currentTimeMillis() - startWait > 2000) {
return "查询 " + queryId + " 等待超时";
}
}
}
try {
// 模拟查询操作
Thread.sleep(new Random().nextInt(300) + 100);
String result = "查询 " + queryId + " 完成,使用连接: " + connection;
return result;
} finally {
// 释放连接
connection.release();
}
}
}
异步IO(AIOC)非阻塞演示
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;
public class AsyncFileRead {
public static void main(String[] args) throws Exception {
Path path = Paths.get("async_data.txt");
// 创建文件
try (FileWriter writer = new FileWriter(path.toFile())) {
for (int i = 0; i < 1000; i++) {
writer.write(i + "\n");
}
}
// 异步读取
AsynchronousFileChannel channel = AsynchronousFileChannel.open(
path,
StandardOpenOption.READ
);
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println("开始异步读取文件...");
// 发起异步读取操作
Future<Integer> result = channel.read(buffer, 0);
// 异步读取期间可以执行其他任务
System.out.println("文件正在读取中,执行其他任务...");
Thread.sleep(1000);
System.out.println("其他任务执行完成");
// 等待读取完成
int bytesRead = result.get();
System.out.println("文件读取完成,共读取:" + bytesRead + " 字节");
buffer.flip();
String content = StandardCharsets.UTF_8.decode(buffer).toString();
System.out.println("文件内容(前100字符):" + content.substring(0, Math.min(100, content.length())));
channel.close();
}
}
关键要点
- BIO(阻塞IO):每个IO操作会阻塞当前线程直到完成
- NIO(非阻塞IO):使用Selector实现多路复用,避免线程阻塞
- AIO(异步IO):通过Future或Callback实现真正的异步操作
- 连接池:解决数据库连接等资源的等待问题
- 线程池:避免频繁创建销毁线程造成的IO等待
这些案例展示了IO等待对性能的影响以及如何使用不同技术来优化,在实际应用中,应根据场景选择合适的IO模型。