Java网络编程
伪异步I/O编程
在文章微服务底层通信和协议--1篇中,我对传统的BIO编程做了介绍,现在来看看伪异步I/O编程。
前面提到的线程暴增,服务器宕机,那么可以使用线程池来管理这些线程,实现一个或多个线程处理N个客户端的请求模型,但是其底层还是使用同步阻塞I/O,通常被称为“伪异步I/O模型”。
如果我们使用缓存线程池,CachedThreadPool线程池,除了能够自动帮我们管理线程(复用)外,看起来就像1:1的客户端线程数模型,而使用FixedThreadPool(固定长度线程池)可以有效的控制线程的最大数量,保证系统有限资源的控制,实现N:M的伪异步I/O模型。但是,正因为限制了数量,如果发生大量并发请求,超过最大数量的线程就只能等待,直到线程池中有空闲的线程可以被复用。
当对Socket的输入流进行读取操作的时候,它会一直阻塞,直到发生下面情况:
- 有数据可读。
- 可用数据以及读取完毕。
- 发生空指针或I/O异常。
所以在读取数据较慢时(比如数据量大、网络传输慢等),大量并发情况下,其他接入的消息只能一直等待,这就是最大的弊端。而后面学习的NIO就可以解决这个问题。
接下来将微服务底层通信和协议--1篇中的ServerBetter类做一下修改,代码如下:
package com.study.socket.weiio;
import com.study.socket.ServerHandler;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/***
* 伪异步I/O编程模式
*/
public class ServerBetter {
private static int DEFAULT_PORT = 1234; //默认的端口号
/**单例的ServerSocket**/
private static ServerSocket server;
/**1. 线程池 懒汉式的单例**/
private static ExecutorService executorService = Executors.newFixedThreadPool(60);
/**根据传入参数设置监听端口,如果没有参数,就调用默认的方法**/
public static void start() throws IOException {
//使用默认端口
start(DEFAULT_PORT);
}
//这里我们使用synchronized,因为这个方法不会被大量访问,不太需要考虑效率,直接进行方法同步就可以
public synchronized static void start(int port) throws IOException {
if (server != null) {
return;
}
try{
//2. 通过构造函数创建ServerSocket,如果端口合法且空闲,服务端就会监听成功
server = new ServerSocket(port);
//3. 通过无限循环监听客户端连接,如果没有客户端接入,将阻塞在accept操作上
while (true){
Socket socket = server.accept();
System.out.println("服务器已经气都没回,端口号为:"+port);
//4. 当有新的客户端接入时,创建一个新的线程处理这条Socket链路
//注释掉旧版的
//new Thread(new ServerHandler(socket)).start();
/**改为新版**/
//从线程池中获取一个新的线程处理这条Socket链路
executorService.execute(new ServerHandler(socket));
}
}finally {
//5. 服务器关闭时,清理相关的资源
if(server != null){
System.out.println("服务器已经关闭!");
server.close();
server = null;
}
}
}
}
以上就是对伪异步I/O编程做的简单介绍,下面的文章将介绍NIO编程