Java BIO

概念

Blocking IO也称为BIO,即同步阻塞IO。Java的io包基于流模型实现,提供了File、FileInputStream、FileOutputStream等输⼊输出流的功能。Java的io包下提供的流操作,交互⽅式是同步且阻塞的⽅式,在输⼊输出流的操作进⾏读、写动作完成之前,线程会⼀直阻塞。因此io包中对流的操作容易造成性能的瓶颈。同样的,在java.net包下提供的部分⽹络API,如Socket、ServerSocket、HttpURLConnection等,进⾏⽹络通信时,也⽤到了java.io包下的流操作,因此也属于同步且阻塞的IO⾏为。

实现逻辑

在BIO同步阻塞模型下,⼀个服务端的可以开启多个处理线程来处理客户端的连接,但是⼀个处理线程只能对应⼀个客户端的连接。

Java BIO_第1张图片

  • 客户端的实现

    package com.my.io.bio;
    ​
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: 客户端1
     * @date 2024/1/24 10:41
     */
    public class SocketClient {
    ​
        public static void main(String[] args) throws IOException {
            // 1. 连接服务端
            Socket socket = new Socket("localhost", 9090);
            // 2. 发送数据
            OutputStream os = socket.getOutputStream();
            os.write("hello bio from client 1".getBytes());
            os.flush();
            // 3.接收服务端返回的数据
            InputStream is = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = is.read(bytes);
            System.out.println("client 1 接收到服务端返回的数据:" + new String(bytes, 0, len));
            // 关闭连接
        }
        
    }
    ​
  • 单线程服务端的实现

    package com.my.io.bio;
    ​
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: 单线程的服务器
     * @date 2024/1/24 10:26
     */
    public class SocketServerSingleThread {
    ​
        public static void main(String[] args) throws IOException {
            // 1.创建服务端的Socket
            ServerSocket serverSocket = new ServerSocket(9090);
            while (true){
                System.out.println("等待客户端的连接");
                // 阻塞等待客户端的连接
                Socket socket = serverSocket.accept();
                System.out.println("已有客户端连接了");
                // 开始处理客户端的读写请求
                InputStream is = socket.getInputStream();
                byte[] bytes = new byte[1024];
                // 阻塞的等待客户端向io流通道中写数据
                int len = is.read(bytes);
                System.out.println("收到客户端的数据: " + new String(bytes, 0, len));
                // 服务端返回信息给客户端
                OutputStream os = socket.getOutputStream();
                os.write("success".getBytes());
                os.flush();
            }
        }
        
    }
    ​
  • 多线程服务端的实现

    为了解决单线程服务端的同步阻塞问题,BIO模型下可以让服务端使⽤多线程来同时处理多个客户端的请求。

    package com.my.io.bio;
    ​
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    ​
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: 多线程服务端
     * @date 2024/1/24 12:38
     */
    public class SocketServerMultipleThread {
    ​
        public static void main(String[] args) throws IOException {
            // 1.创建服务端的Socket
            ServerSocket serverSocket = new ServerSocket(9090);
            while (true){
                System.out.println("等待客户端的连接");
                // 阻塞等待客户端的连接
                Socket socket = serverSocket.accept();
                System.out.println("已有客户端连接");
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            handle(socket);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
    ​
                    }
                }).start();
            }
        }
        
        private static void handle(Socket socket) throws IOException {
            // 开始处理客户端的读写请求
            InputStream is = socket.getInputStream();
            byte[] bytes = new byte[1024];
            // 阻塞的等待客户端向io流通道中写数据
            int len = is.read(bytes);
            System.out.println("收到客户端的数据:" + new String(bytes, 0, len));
            // 服务端返回信息给客户端
            OutputStream os = socket.getOutputStream();
            os.write("success".getBytes());
            os.flush();
        }
        
    }
    ​

BIO的局限

在上⾯的例⼦中可以看出,IO代码⾥Read操作是阻塞操作,如果连接不做数据读写操作,会导致线程阻塞,浪费线程资源。

除此之外,如果客户端请求过多,将会导致服务端不断创建出新的线程,对服务器造成过⼤压⼒。

总结:

1 每个socket接收到,都会创建一个线程,线程的竞争,切换上下文影响性能。

2 每个线程都会占用栈空间和CPU资源

3 并不是每个socket都进行IO操作,无意义的线程处理;

4 客户端的并发访问量增加时,服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出;线程创建失败,最终导致进程宕机或僵死,从而不能对外提供服务。

BIO的应用场景

BIO⽅式的使用和维护相对比较简单,适⽤于较小且固定的应用框架。

你可能感兴趣的:(java,开发语言)