java 的 IO 演进之路
我们在前面学习了 linux 的 5 种 I/O 模型详解
下面我们一起来学习下如何使用 java 实现 BIO/NIO/AIO 这 3 种不同的网络 IO 模型编程。
BIO 编程
BIO 作为最基础的 IO 版本,实现起来比较简单。
Server
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* <p> BIO 服务端 </p>
* @author 老马啸西风
*/
public class TimeServer {
public static void main(String[] args) throws IOException {
final int port = 8088;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("server started at port " + port);
// 循环监听
while (true) {
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功");
// 读取客户端的信息
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Server Recevie: " + bufferedReader.readLine());
// 读取客户端的信息
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
String currentTime = System.currentTimeMillis()+"";
printWriter.println(currentTime);
}
}
}
client
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* <p> BIO 客户端 </p>
*
* @author 老马啸西风
*/
public class TimeClient {
public static void main(String[] args) throws IOException {
final int port = 8088;
try(Socket clientSocket = new Socket("127.0.0.1", port)) {
System.out.println("Client started at port " + port);
// 写入信息
PrintWriter printWriter = new PrintWriter(clientSocket.getOutputStream(), true);
printWriter.println("hello bio");
// 读取反馈
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
System.out.println("client recevie: " + bufferedReader.readLine());
}
}
}
启动测试
- 启动服务端
server started at port 8088
- 启动客户端
Client started at port 8088
client recevie: 1568643464491
Process finished with exit code 0
- 再次查看服务端日志
server started at port 8088
客户端连接成功
Server Recevie: hello bio
线程池版本
BIO 的缺点
缺点其实非常明显,每次都要创建一个线程去处理。
比如我的实现是直接阻塞当前线程的,这当然非常的不友好。
可以使用线线程池的方式进行优化改进。
线程版本
public class TimeThreadServer {
public static void main(String[] args) throws IOException {
final int port = 8088;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("server started at port " + port);
// 循环监听
while (true) {
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功");
new ServerHandler(socket).start();
}
}
static class ServerHandler extends Thread {
private final Socket socket;
ServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 读取客户端的信息
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Server Recevie: " + bufferedReader.readLine());
// 读取客户端的信息
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
String currentTime = System.currentTimeMillis()+"";
printWriter.println(currentTime);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
线程池版本
public static void main(String[] args) throws IOException {
final int port = 8088;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("server started at port " + port);
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 循环监听
while (true) {
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功");
// 线程池处理
executorService.submit(new ServerHandler(socket));
}
}
其他代码保持不变。
优缺点
线程池版本的 BIO 又被称作伪异步 IO。
属于在 NIO 还没有流行之前的一种实战解决方案。
这种方式的性能和 BIO 想比较提升了很多,实现起来也比较简单,但是可靠性相对较差。
参考资料
《Netty 权威指南》