IO到NIO的一个转变

本内容来源:Jack视频讲解和自己的一个理解。

1、故事还得从网络模型或者IO开始聊起

2、你有想过传统IO真正存在的问题吗?

3、如果你是设计者,IO可以怎样改进?

4、NIO原理分析以及代码实现

IO:不就是input和output吗。input:输入操作;output:输出操作;从jdk出生开始他就已经存在了,在jdk的java.io包下面。

我们都知道io是阻塞 io,那么想一下为什么是阻塞的?来看看下面这个IO的一个测试类

package com.test;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * @Title: BIOServer
 * @Description: IO 测试
 * @author: sunxuesong@hztianque.com
 * @date: Created in 20:05 2019/9/8
 * @Modifired by:
 */
public class BIOServer {
    public static void main(String[] args) {
        try{
            // socket监听,端口8888
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("BIOServer has started,listening on port:" + serverSocket.getLocalSocketAddress());
            // 不停的监听有没有客户端进行链接
            while (true) {
                // 进行链接
                Socket clientSocket = serverSocket.accept();
                System.out.println("Connect from " + clientSocket.getRemoteSocketAddress());
                try {
                    // 客户端输入的内容
                    Scanner input = new Scanner(clientSocket.getInputStream());
                    // 这个while是用来客户端和服务器进行交互的,通过输出流进行数据写出
                    while (true) {
                        String request = input.nextLine();
                        if ("quit".equals(request)) {
                            break;
                        }
                        System.out.println(String.format("From %s : %s", clientSocket.getRemoteSocketAddress(), request));
                        String response = "From BIOServer Hello:" + request +".\n";
                        clientSocket.getOutputStream().write(response.getBytes());
                    }
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

启动上面的main方法服务进行测试一下:

打开cmd 输入 telnet localhost 8888 回车,链接进来后,随便输入然后回车可以在控制台看到你输入的内容,这时候在来重新打开一个cmd,之前的cmd窗口不要关闭,这时候可以链接进来,但是这时候不管你怎么输入控制台都不会有反应,这是为什么呢?因为被阻塞了,在队列中进行等待,如果你关闭之前的cmd窗口,这时候控制台会立马看到你输入的内容。那既然这样就会有人说了我们可以使用多线程去操作,这种想法很好,在tomcat1.7之前tomcat也是这么做的,比如我用一个线程池大小是3,这时候最多支持3个客户端链接可以进行交互,如果第四个来了那么只能阻塞等待,因为前面的线程没有进行释放。

来看一下IO的模型:

1.阻塞io(Blocking I/O)

我来描述一下怎么请求的:首先用户去请求读取磁盘数据,这时候磁盘数据是受保护的不允许你直接去读取,它必须通过内核空间去读取(这是计算机自己的一个机制,分为用户空间和内核空间,用户空间就是我们的java进程,内核空间是计算机自己分配的),那么java是怎么做的呢?将请求交给内核空间,其他不用你去管。那么当java进程去内核空间拿数据的时候,这时候发现内核空间还没有将数据准备好,那他就会一直等待着,直到你准备好了为止。这时候等待的状态就是一个阻塞的状态,他不会释放cpu的资源。其他线程无法进行访问。

2:非阻塞IO(No Blocking I/O)

这个是怎么用的呢?我来解释一下他和上面的区别主要在于他不会去等待内核空间,当内核空间没有准备好数据的时候他直接返回了,不会占用资源。

3:复用IO

这种IO他不会一直等待,他有一个专门的线程来管理所有的客户端请求。他会一直轮询,知道发现有一个socket是read状态然后进行数据的一个读取。

这时候NIO就孕育而生了,他对上面的几个模型进行了一个取长补短的操作。

首先基于这种高速的请求,那么客户端的链接速度肯定是跟不上的,于是nio发明了selector的思想,多路复用的一个概念,多路:多个TCP链接(Socket或者Channel)。复用:指的是多个链接复用了一个线程,这里一个线程指的是selector轮询机制。

多路复用:当一个或多个客户端进行链接的时候,统一由多路复用器进行管理(一个单独的线程)这时候由内核进行监视多路复用器里面的socket,这时候用户进程是阻塞的,当任何一个socket数据准备好了,多路复用器都会返回,这时候用户进程再调用read操作把数据从内核缓存区拷贝到用户空间。

下节用源码说明一下nio的一个机制。他用到了复用IO的selector机制,然后他发现内核空间进行复制磁盘数据的时候也是需要等待的,这时候他又引入了一个buffer的概念,buffer是一个可变的缓冲池,默认是1024个字节,当发现buffer中的缓冲池不够的时候会自动的扩容。

posted on 2019-09-10 18:46  冰龙之剑  阅读(314)  评论(0编辑  收藏  举报

导航