1.为了解决同步阻塞I/O编程面临的一个链路需要一个线程处理的问题,我们可以对线程模型进行优化 ----- 后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调用线程资源,设置线程的最大值,防止海量并发接入导致线程耗尽。
2.伪异步IO模型图
代码演示:
3. TimeServer.java
package com.pats.file.io.up;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import com.pats.file.io.TimeServerHandler;
public class TimeServer {
public static void main(String[] args) {
int port = 8080;
if(args!=null && args.length > 0) {
port = Integer.valueOf(args[0]);
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
System.out.println("the time server is start in port:" + port);
Socket socket = null;
TimeServerHandlerExcutePool singleExcutor = new TimeServerHandlerExcutePool(50,10000);
while(true){
socket = server.accept();
singleExcutor.execute(new TimeServerHandler(socket));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(server != null) {
System.out.println("The time server close");
try {
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
server = null;
}
}
}
}
伪异步IO的主函数代码发生了变化,我们需要创建一个时间服务器处理类的线程池,当接收到新的客户端连接时,将请求Socket封装成一个Task,然后调用线程的execute方法,从而避免了每个请求接入都创建一个新的线程。
4. TimeServerHandlerExcutePool.java
package com.pats.file.io.up;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TimeServerHandlerExcutePool {
private ExecutorService executor;
public TimeServerHandlerExcutePool(int maxPoolSize, int queueSize) {
executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),maxPoolSize,
120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
}
public void execute(Runnable task) {
executor.execute(task);
}
}
由于线程池和消息队列是有界的,因此,无论客户端并发连接数多大,都不回导致线程数过于膨胀或者内存溢出。
5. TimeServerHandler.java
package com.pats.file.io.up;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
public class TimeServerHandler implements Runnable{
private Socket socket;
public TimeServerHandler(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;
}
System.out.println("The time server receive order:"+body);
currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)? new Date(System.currentTimeMillis()).toString() :"BAD ORDER";
out.println(currentTime);
}
} catch (IOException e) {
if(in != null) {
try {
in.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
if(out != null) {
out.close();
out = null;
}
if(this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
6.客户端代码与同步阻塞BIO编程相同
TimeClient.java
package com.pats.file.io.up;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TimeClient {
public static void main(String[] args) {
int port = 8080;
if(args!=null && args.length > 0) {
port = Integer.valueOf(args[0]);
}
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1",port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
out.println("QUERY TIME ORDER");
System.out.println("Send order 2 server succeed");
String resp = in.readLine();
System.out.println("Now is: " + resp);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(out != null) {
out.close();
}
if(in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
7.启动 执行
服务端
客户端
8.伪异步I/O只是对之前的I/O线程模型的一个简单优化,由于读和写都是同步阻塞的,它无法从根本上解决同步I/O导致的通信线程阻塞问题,当通信时间过长,会引发一下问题。
(1).如果发送数据才需要60s,那么服务端也会同步阻塞60s。
(2).假如所有的线程都被阻塞,那么后续所有的I/O消息都将在队列中排队,客户端会发生大量连接超时。
(3).当所有的连接都超时,调用者会认为系统已经崩溃,无法接收新的请求消息。