深入分析Java Web之 Java I/O的工作机制

在这里插入图片描述

1. 磁盘I/O的工作机制

1.1 几种访问文件的方式

读取和写入文件I/O操作都是调用操作系统提供的接口,系统调用是在内核空间进行的,而用户程序的操作是在用户空间进行,所以磁盘IO是先将数据从磁盘复制到内核空间,再从内核空间复制到用户空间;
为了加速IO的访问,操作系统在内核空间使用缓存机制,把从磁盘读取的文件按照一定的组织方式进行缓存,如果用户程序访问的是同一段磁盘地址的空间数据,那么操作系统就直接去内核缓存中取数据返回给用户程序,减少了取磁盘读取的操作

1.1.1 标准访问文件的方式

在这里插入图片描述
读:标准文件访问的方式就是如上所述,应用程序调用read()接口,操作系统先去内核的缓存中查找数据,有则直接返回,没有就从磁盘读取,缓存到操作系统内核的缓存

写:应用程序调用write()接口把数据从用户地址空间复制到内核地址空间的缓存,操再由作系统将数据写入磁盘

1.1.2 直接IO

在这里插入图片描述
不经过操作系统的内核缓冲区,减少了一次由内核缓冲区到用户程序缓存的数据的复制
通常使用在对数据缓存管理由应用程序实现的数据库管理系统中

1.1.3 同步访问文件

只有数据被成功写入磁盘才返回给程序成功的标志

1.1.4 异步访问

不会阻塞等待

1.1.5 内存映射的方式

在这里插入图片描述
操作系统将内存中的某一块区域和磁盘中的文件关联,当要访问磁盘中的一段数据转换为访问文件中的某一段数据,减少了内核空间缓存到用户空间缓存的数据复制操作

1.2 Java访问磁盘文件

在这里插入图片描述
首先创建的File并不是真正的文件对象,而是一个代表该路径的虚拟对象,当创建FileInputStream对象的时候内部会创建一个FileDescripor对象才是真正代表一个存在的文件对象的描述,同时这个对象可以直接控制磁盘文件

2. 网络I/O的工作机制

2.1 Java Socket

在这里插入图片描述
主机A的应用程序通过Socket和主机B的应用程序建立连接进行通信,通过一个Socket实例唯一标识一个主机上的应用程序的通信链路
对于客户端来说,客户端创建以恶搞Socket实例,操作系统将这个Socket实例分配给指定的端口号,创建一个包含本机IP,远程地址和端口号的套接字数据结构,然后进行TCP三次握手建立连接,创建完成Socket实例对象

3. NIO的工作方式

3.1 BIO的问题

BIO是阻塞IO,会让线程失去CPU的使用权,这在当前的大规模访问量和有性能要求的情况下不能被接收

在不考虑多线程的情况下BIO无法处理并发,因为他会阻塞等待用户的输入,这个时候就不会再去建立新的客户的连接

		byte[] bytes = new byte[1024];
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(8080));
        while(true){
            //阻塞 等待客户的的连接
            Socket socket = serverSocket.accept();
            //阻塞 等待客户端输入
            int read = socket.getInputStream().read(bytes);
        }

3.2 解决

可以使用多线程,为每一个连接开一个线程,但是并不是每一个线程的用户端都发送数据,没有必要为了这么多不活跃的用户端去开新线程

所以我们要使用单线程去解决,也就是说我们想把两个阻塞的地方都设置成非阻塞的
① 等待客户端输入这部分,我们将他设计成非阻塞的,也就是说即使没有读取到数据也可以向下进行
② 等待客户的的连接,我们也将其设计为非阻塞的,不管有没有连接都会继续执行

大致思路就是这样

 byte[] bytes = new byte[1024];
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(8080));
        //非阻塞 伪代码
        serverSocket.setConfig(false);
        //存放所有建立连接的socket
        List<Socket> list = new ArrayList<>();
        while(true){
            //上面设置成了非阻塞
            Socket socket = serverSocket.accept();
            if(socket == null){//没人连接
                for (Socket socket1 : list) {
                    //从客户端读数据也设置成了非阻塞
                    int read = socket1.getInputStream().read(bytes);
                    if(read!=0){
                        //logic
                    }
                }
            }else {//有人连接
                //从客户端读数据设置成非阻塞
                socket.setConfig(false);
                list.add(socket);
                for (Socket socket1 : list) {
                    //从客户端读数据也设置成了非阻塞
                    int read = socket1.getInputStream().read(bytes);
                    if(read!=0){
                        //logic
                    }
                }
            }

上面就是NIO的大致设计思路吗,只不过NIO中的轮询也就是socket的for循环不是由应用系统来执行的,这样效率太低,我们通过操作系统主动感知有消息的socket再反馈给应用程序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值