IO简介

IO即为Input/Output,IO可分为两种磁盘IO和网络IO。顾名思义,磁盘IO指的是从磁盘中进行输入和输出,也就是磁盘数据的读取和存储;而网络IO便指的是网络数据的读取和写入,具体的数据读取方式则是前文提到的JAVA网络编程之Socket。本系列主要内容为java网络编程IO系列,涵盖java中的BIO、NIO和AIO三种模型。对于IO模型不太了解的建议先看一下Linux下的五种网络IO模型

BIO概念

起初,java中的IO操作,都是基于BIO的,BIO原意为Base IO ,因为是最早使用的,也是IO调用的基础用法,但因为BIO会阻塞当前线程B又被理解为Block,即阻塞IO,也是现在通用的说法。传统BIO执行时,执行方式为同步阻塞的,当前线程会等待IO调用执行完成,返回结果,这个会阻塞当前线程,此时cpu会保存当前线程上下文,进行线程切换,执行其他就绪线程。线程切换是需要耗费cpu资源的,频繁的切换线程会导致cpu资源的极大浪费。

BIO处理流程

为了支持并发,服务器通常会采用多线程并发处理请求。

即有新连接进入时,创建新线程,在新线程中进行IO和计算处理。过程如下所示:

java 安装 磁盘空间 java 磁盘io_服务器


通常为了避免频繁创建和销毁线程,服务器会使用线程池进行线程调度和管理。

BIO实现

/**
* BIO服务端
**/
public class BIOServer {
   static ExecutorService serverService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws IOException {
    	//定义服务端socket,监听9090端口
        ServerSocket serverSocket = new ServerSocket(9090);
        //死循环接收客户端请求
        while (true) {
        	//祖舍获取连接
            Socket socket = serverSocket.accept();
           //获取新连接,提交线程池处理
           serverService.submit(new ServerRequestHandler(socket));
        }
    }

}
/**
* 处理请求
**/
public class ServerRequestHandler implements Runnable {
    private Socket socket;

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

    public void run() {
        try {
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            System.out.println("收到消息:" +dis.readUTF());
            DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
            dos.writeUTF("消息已收到");
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
* 简易客户端
**/
public class Client {
    public static void main(String[] args) throws IOException {
        for (int i=1;i<5;i++) {
        // 构造客户端socket,连接本地9090端口
            Socket socket = new Socket("localhost", 9090);
            DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
            dos.writeUTF("这是来自客户端" + i + "的请求");
            DataInputStream dis=new DataInputStream(socket.getInputStream());
            System.out.println("服务端响应:"+dis.readUTF());
            socket.close();
        }
    }
}

总结

从处理流程图及简易代码实现可以看出,一个连接会分配一个线程处理,而我们的线程资源是异常宝贵的,不可能无限多,这样一来线程数就限制了服务器的并发。当线程内的请求处理的越慢,服务端可处理的并发请求就越低。而在线程内进行数据的读取和写入IO操作又是阻塞的,进而使得单个线程处理请求的时间拉长,最终导致服务器的整体并发能力不足。当需要满足更高并发的要求时,就要使用其他的IO模式了。