引述
对于前文的传统 BIO,每次有一个连接都需要开启一个线程来处理,这样做无法去控制线程的开启数量,可能会导致服务的宕机。
为了解决前面提到的 BIO 的每一个连接都需要一个线程处理的问题,有人提出了一个解决方案,用线程池的方式对线程进行管理,防止由于海量并发接入导致线程耗尽,实现系统的可控。
代码
服务端
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class Server {
public static void main ( String [] args ) throws IOException {
ServerSocket server = null ;
try {
server = new ServerSocket ( 8080 ); // 创建服务端连接
ServerHandlerExecutePool executor = new ServerHandlerExecutePool ( 50 , 10000 ); // 创建 I/O 任务线程
Socket socket = null ;
while ( true ) {
socket = server . accept (); // 阻塞,直到有连接建立
executor . execute ( new ServerHandler ( socket )); // 在线程池中获取一个线程处理连接
}
} finally {
if ( server != null ) {
server . close ();
server = null ;
}
if ( socket != null ) {
socket . close ();
socket = null ;
}
}
}
}
// 线程池
class ServerHandlerExecuePool {
private ExecutorService executor ;
// maxPoolSize 最大线程数,queueSize 阻塞队列的最大空间
public ServerHandlerExecuePool ( int maxPoolSize , int queueSize ) {
// 初始化线程数,最大线程数,空闲线程存活时间,存活单位,阻塞队列
executor = new ThreadPoolExecutor ( Runtime . getRuntime (). availableProcessors (), maxPoolSize , 120L , TimeUnit . SECONDS , new ArrayBlockingQueue < Runnable >( queueSize ));
}
public execute ( Runnable task ) {
executor . execute ( task );
}
}
// 执行连接
class ServerHandler implements Runnable {
private Socket socket ;
public ServerHandler ( Socket socket ) {
this . socket = socket ;
}
@Override
public void run () {
BufferedReader in = null ;
PrintWriter out = null ;
try {
in = new BufferedReader ( new InputStreamReader ( this . socket . getInputStream ()));
out = new PrintWriter ( this . socket . getOutputStream (), true );
String currentTime = null ;
String body = null ;
while ( true ) {
body = in . readLine ();
if ( body == null )
break ;
out . println ( body );
}
} catch ( Exception e ) {
e . printStackTrace ();
} finally {
if ( in != null ) {
try {
in . close ();
} catch ( IOException e ) {
e . printStackTrace ();
}
}
if ( out != null ) {
try {
out . close ();
} catch ( IOException e ) {
e . printStackTrace ();
}
}
}
}
}
客户端
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
public class Client {
public static void main ( String [] args ) {
Socket socket = null ;
BufferReader in = null ;
PrintWriter out = null ;
try {
socket = new Socket ( "127.0.0.1" , 8080 ); // 建立连接,发起连接请求
in = new BufferReader ( new InputStreamReader ( socket . getInputStream ())); // 监听此缓冲区
out = new PrintWriter ( socket . getOutputStream (), true );
out . println ( "QUERY TIME ORDER" ); // 向服务器端发送指令
String s = in . readLine (); // 从缓冲区中获取数据
} catch ( Exception e ) {
e . printStackTrace ();
} finally {
// 优雅退出
if ( out != null ) {
try {
out . close (); // 释放缓冲区
out = null ;
} catch ( Exception e1 ) {
e1 . printStackTrace ();
}
}
if ( in != null ) {
in . close (); // 释放缓冲区
in = null ;
}
if ( this . socket != null ) {
try {
this . socket . close (); // 关闭连接
} catch ( Exception e1 ) {
e1 . printStackTrace ();
}
this . socket = null ;
}
}
}
}
问题
改进后的 BIO 提高了线程数量的可控性,但还是不满足长连接的需求场景。因为一旦建立多个长连接,线程池的线程也都被占用,此时其他的连接只能防止阻塞队列中。由于 TCP 连接的特性,发送方在收不到响应时会重复发送请求,迟迟收不到响应时会认为网络拥堵,将逐渐减少滑档窗口的大小,直到关闭滑动窗口,使网络崩溃。