java 的 IO 演进之路

我们在前面学习了 linux 的 5 种 I/O 模型详解

下面我们一起来学习下如何使用 java 实现 BIO/NIO/AIO 这 3 种不同的网络 IO 模型编程。

BIO 编程

BIO 作为最基础的 IO 版本,实现起来比较简单。

输入图片说明

Server

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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()); } } }

启动测试

  • 启动服务端
  [plaintext]
1
server started at port 8088
  • 启动客户端
  [plaintext]
1
2
3
4
Client started at port 8088 client recevie: 1568643464491 Process finished with exit code 0
  • 再次查看服务端日志
  [plaintext]
1
2
3
server started at port 8088 客户端连接成功 Server Recevie: hello bio

线程池版本

BIO 的缺点

缺点其实非常明显,每次都要创建一个线程去处理。

比如我的实现是直接阻塞当前线程的,这当然非常的不友好。

可以使用线线程池的方式进行优化改进。

线程版本

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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(); } } } }

线程池版本

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
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 权威指南》

TCP Socket