IO模型之BIO、NIO详讲(通俗易懂) IO模型

IO模型

简单地理解,IO模型就是用什么样的通道进行数据的发送和接收,IO模型很大程度上决定了程序通信的性能

java共支持三种网络编程模型:BIO、NIO、AIO

BIO

同步并阻塞(blockingIO),服务端实现模式为一个连接一个线程,即一个客户端请求服务端就需要启动一个线程来进行处理,并且这个线程在读数据时会堵塞,具体跟accept()和read()方法有关。下面直接上代码讲解

:所谓的同步,就是指所有操作都由当前线程来处理,亲力亲为。而异步呢,是指将操作交由其它线程来处理,其它线程处理完毕之后,返回处理的结果

代码

服务端代码

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
csharp复制代码public class BIOserver {
   public static void main(String[] args) throws IOException {
       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS,new ArrayBlockingQueue<>(8));
       // 绑定端口
       ServerSocket serverSocket = new ServerSocket(6666);
       while(true){
           // 监听端口
           System.out.println("线程"+ Thread.currentThread().getName()+"正在监听端口6666......");
           Socket accept = serverSocket.accept();
           System.out.println("连接已建立!");
           // 一监听到请求就启动线程进行处理
           threadPoolExecutor.execute(new Runnable() {
               @Override
               public void run() {
                   System.out.println("启动线程"+Thread.currentThread().getName()+"处理请求");
                   try {
                       sockerHandler(accept);
                  } catch (IOException e) {
                       e.printStackTrace();
                  }
              }
          });
      }
  }

   public  static void sockerHandler(Socket socket) throws IOException {
       byte[] bytes = new byte[1024];
       // 从管理中获得流
       System.out.println("线程"+Thread.currentThread().getName()+"正在等待连接发送请求......");
       InputStream inputStream = socket.getInputStream();
       while(true) {
           // 将流写到bytes里,打印出来
           int read = inputStream.read(bytes);
           if (read != -1) System.out.println(new String(bytes, 0, read));
           else {
               break;
          }
      }
  }
}

客户端代码

1
2
3
4
5
6
7
8
9
ini复制代码public class BIOclient {
   public static void main(String[] args) throws IOException {
       Socket socket = new Socket();
       socket.connect(new InetSocketAddress(6666));
       OutputStream stream = socket.getOutputStream();
       String str="Hello,world!";
       stream.write(str.getBytes());
  }
}

运行

先启动服务端,可以看到,服务端启动后就阻塞在accept()方法上,等待客户端的连接。如果没有连接,则会一直阻塞

image-20211031210839784.png

紧接着,我们打上断点,启动客户端来建立与服务端的连接。如下,可以看到,建立完连接之后,服务端阻塞在read()方法上,等待读取数据。如果客户端没有发送数据,则thread-1会一直阻塞在read()上

image-20211031212403663.png

接着让客户端断点往下走,则服务端读取到数据,打印出来

image-20211031212506956.png

我们再启动一个客户端,继续上面的流程。可以看到,这时候创建了一个新的线程thread-2,如果我们启动100个客户端,那么服务端自然也就要创建100个线程来处理客户端的请求咯

image-20211031212725695.png

分析

从上面的分析可以看到,在accept()等待连接时会阻塞,在read()等待读取数据时也会阻塞,所以我们必须对每个客户端创建一个线程,否则accept()的阻塞会影响到对其它客户端的读操作。

一个连接就要创建一个线程,10万个连接就要创建10万个线程,这样显然是不合理的,因为服务器没有那么多的资源来分配给线程。另一个方面,如果建立连接之后,线程没有数据可读,那么就会阻塞在read操作上,造成线程资源的浪费。

image-20210603205309802.png
其实整个问题的根源就在于accept()和read()的阻塞,也就是所谓优化点,接下来我们引入NIO

NIO(未完待续)

NIO,即non-blocking IO,同步非阻塞IO,是指从JDK1.4开始,java提供的一系列改进的输入/输出新特性,NIO也称为new IO。NIO的三大核心部分分别为:Channel、Buffer和Selector

对于所谓的IO流,刚开始学习java IO时我一直都无法理解,为什么要叫IO流呢?后面不断深入学习才开始理解

要理解IO流,我们可以通过水流来类比。在生活中,水流通常是从供水站经过水管输送到居民家中,我们可以把IO流比作水管,传输的数据比作水管中的水,数据源和数据接收方则分别对应供水站和居民。

image-20211031221638725.png

而所谓的输入、输出流呢,其实都是相对于当前主机的内存而言的。如果把数据从磁盘或网卡输入到内存,那么就要使用输入流;如果把数据从内存输出到网卡或者磁盘,那么就要使用输出流。即,输入内存用输入流,输出内存用输出流!

image-20211031222123520.png

image-20211031222210254.png

明天继续更新……

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%