一个简单的伪异步IO程序

为了解决同步阻塞IO面临的一个请求需要一个线程处理的问题,后来有人对它的线程模型进行了优化,通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远大于N。通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量请求并发接入导致线程耗尽。
下面我们通过一个示例来说明

服务端代码

TimeServer

public class TimeServer {

    public static void main(String[] args) throws IOException {
        ServerSocket server = null;
        try {
            // 如果端口未被占用且合法则创建成功
            server = new ServerSocket(8080);
            Socket socket = null;
            // 创建线程池
            TimeServerHandlerExecutePool executePool = new TimeServerHandlerExecutePool(50,10000);
            // 通过无限循环来监听客户端连接,如果没有客户端接入,主线程阻塞在accept操作上
            while (true) {
                socket = server.accept();
                // 将请求任务丢到线程池中进行处理
                executePool.execute(new TimeServerHandler(socket));
            }
        } catch (Exception e) {
            System.out.println("创建serverSocket失败,端口:" + 8080);
            e.printStackTrace();
        } finally {
            System.out.println("The time server close");
            if (server != null) {
                server.close();
            }
        }
    }
}

服务端创建一个ServerSocket,然后通过无限循环的方式来监听客户端连接,如果没有客户端接入,则主线程会阻塞在accept操作上。定义一个线程池用来处理客户端请求任务,当有客户端连接接入后,将socket组装成TimeServerHandler,这是个Runnable,丢到线程池中进行处理
TimeServerHandler

public class TimeServerHandler implements Runnable {

    private Socket socket;

    public TimeServerHandler(Socket socket) {
        this.socket = socket;
    }

    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;
            String body;
            while (true) {
                // ★ 读取一行,如果读到输入流尾部,则返回值为null,退出循环
                body = in.readLine();
                if (null == body) {
                    break;
                }
                System.out.println("The time server receive order : " + body);
                // 如果读到了非空值,对内容进行判断,如果请求消息为查询时间的指令 QUERY TIME ORDER,则获取当前系统时间
                // 通过PrintWriter的println函数发送给客户端,然后退出循环
                if ("QUERY TIME ORDER".equalsIgnoreCase(body)) {
                    currentTime = new Date(System.currentTimeMillis()).toString();
                } else {
                    currentTime = "BAD ORDER";
                }
                // ★ 通过printWriter发送给客户端
                out.println(currentTime);
            }
        } catch (IOException e) {
            System.out.println("exception");
            e.printStackTrace();
            // 释放输入流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            // 释放输出流
            if (out != null) {
                out.close();
            }
            // 释放socket套接字句柄资源
            if (this.socket != null) {
                try {
                    this.socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}

TimeServerHandlerExecutePool

public class TimeServerHandlerExecutePool {

    private ExecutorService executorService;

    public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) {
        this.executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize,
                120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
    }

    public void execute(Runnable task) {
        this.executorService.execute(task);
    }
}

客户端代码

TimeClient

public class TimeClient {

    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            //
            socket = new Socket("127.0.0.1", 8080);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
            // ★ 通过PrintWriter向服务端发送 QUERY TIME ORDER 指令
            out.println("QUERY TIME ORDER");
            System.out.println("send order to server succeed.");
            // ★ 通过BufferedReader读取响应并打印
            String response = in.readLine();
            System.out.println("Now is : " + response);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放输入流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            // 释放输出流
            if (out != null) {
                out.close();
            }
            // 释放socket套接字句柄资源
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}

分别执行服务端和客户端,执行结果如下:
服务端执行结果:

The time server receive order : QUERY TIME ORDER

客户端执行结果:

send order to server succeed.
Now is : Sun Jul 19 16:48:30 CST 2020

到此伪异步IO的示例程序讲解完毕

Talk is cheap , show me the picture
最后通过一张图来说明伪异步IO的通信模型
在这里插入图片描述

总结

由于线程池是有界的,无论客户端并发连接数多大,都不会导致线程个数过于膨胀或者内存溢出,相比传统的BIO是一种改良。但仍然无法解决同步IO导致的通信线程阻塞问题

参考资料

  • 《Netty权威指南》第二版 by 李林锋
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值