伪异步 IO

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

伪异步 IO 模型图

采用线程池和任务队列,可以实现一种叫做伪异步的 I/O 通信框架,它的模型图如上图所示。当有新的客户端接入时,将客户端的 Socket 封装成一个 Task(该任务实现 java.lang.Runnable 接口),然后投递到后端的线程池中进行处理,JDK 的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。

伪异步 I/O 通信框架采用了线程池实现,因此避免了为每个请求都创建一个独立线程所造成的线程资源耗尽问题。不过因为它的底层仍然是同步阻塞的 BIO 模型,所以还是无法从根本上解决问题。

代码示例

下面代码中,演示了 BIO 通信(一请求一应答)模型。我们会在客户端创建多个线程,然后依次连接服务端,并向其发送 "当前时间+:hello world",服务端会为每个客户端线程创建一个线程来处理。代码示例出自:闪电侠的博客

客户端

public class IOClient {

  public static void main(String[] args) {
    // TODO 创建多个线程,模拟多个客户端连接服务端
    new Thread(() -> {
      try {
        Socket socket = new Socket("127.0.0.1", 3333);
        while (true) {
          try {
            socket.getOutputStream().write((new Date() + ": hello world").getBytes());
            Thread.sleep(2000);
          } catch (Exception e) {
          }
        }
      } catch (IOException e) {
      }
    }).start();
  }

}

服务端

public class IOServer {

  public static void main(String[] args) throws IOException {
    // TODO 服务端处理客户端连接请求
    ServerSocket serverSocket = new ServerSocket(3333);

    // 接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理
    new Thread(() -> {
      while (true) {
        try {
          // 阻塞方法获取新的连接
          Socket socket = serverSocket.accept();
          // 每一个新的连接都创建一个线程,负责读取数据
          new Thread(() -> {
            try {
              int len;
              byte[] data = new byte[1024];
              InputStream inputStream = socket.getInputStream();
              // 按字节流方式读取数据
              while ((len = inputStream.read(data)) != -1) {
                System.out.println(new String(data, 0, len));
              }
            } catch (IOException e) {
            }
          }).start();

        } catch (IOException e) {
        }
      }
    }).start();
  }

}

总结

在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O ,编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,这时候就需要一种更高效的 I/O 处理模型,来应对更高的并发量。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 为了解决同步阻塞IO面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化,后端通过一个线程池来处...
    FX_SKY阅读 490评论 0 2
  • 请先移步查看《Java 传统的BIO编程》 我们将客户端请求投递到线程池中进行处理,JDK线程池维护一个消息队列和...
    直到世界的尽头_yifan阅读 962评论 0 1
  • BIO:Accept线程分发 伪异步IO:线程池 NIO:缓冲区Buffer通道Channel多路复用Select...
    Martin_阿锤阅读 702评论 0 0
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,148评论 1 32
  • 虚空让med出肉,他说给他拖30分钟。 队友心力憔悴地拖沓到了三十分钟的夜晚,虚空退了。他们咒骂着这报复社会的举动...
    呜喵王怒风阅读 166评论 0 0