Java IO模型 BIO,NIO,AIO 详解

  • 阻塞式IO模型,BIO 

   JDK1.4 之前都是采用BIO模式(blocking I/O) ,阻塞式IO,模型如图

解释: 应用程序需要从磁盘读取数据分为两个阶段,1将磁盘数据复制到内核,2将内核数据复制到应用程序空间:

  1. 准备数据:应用程序问cpu说:我需要一个aaa.txt 文件,你去给我取来。CPU告诉应用程序,你等着我去给你准备数据(应用程序一直傻傻等待)。CPU转身问磁盘:磁盘我需要aaa.txt 文件,磁盘开始准备数据,并将数据复制到内核空间中(耗时)
  2.  等磁盘把数据给到CPU之后,告诉应用程序数据准备好了,现在给你,CPU将数据从内核复制到用户空间(耗时)

在上述两个阶段中,对于应用程序的一个线程来说发出读取文件指令后,这个线程都是一直在等待(阻塞),傻傻什么都不做,如果要想处理其他事情就需要其他线程来做。很明显这样的BIO模型性能很低,且消耗资源

jdk中java.io.* 包中都是基于阻塞式io实现的

代码:

public class BioMain {
     public static void main(String[] args) throws IOException {
        //服务端创建
        ServerSocket serverSocket = new ServerSocket(1234);
        System.out.println("服务端启动,等待连接");

        //可以一直接收客户端连接,但是只能有一个客户端可以读取数据
        while (true) {
            //accept阻塞,serverSocket等待客户端的连接
            Socket accept = serverSocket.accept();
            System.out.println("来自客户端的连接:" + accept.getRemoteSocketAddress());

            InputStream inputStream = accept.getInputStream();

            Scanner scanner = new Scanner(inputStream);
            //针对一个客户端socket连接进行IO操作
            while (true) {
                String line = scanner.nextLine();//阻塞,一直等待数据进来
                if ("q".equals(line)){
                    break;
                }
                System.out.println("收到数据:" + line);
                accept.getOutputStream().write("I am server ask:".getBytes());
            }
        }
    }
}

 上述代码中只有一个main 线程,也就只能处理一个连接的请求,如果想处理多个请求,我们可以在服务端采用多线程的方式,也就对每个socket连接都分配一个线程

代码2:

public class BioMain2 {
    public static void main(String[] args) throws IOException {
        //服务端创建
        ServerSocket serverSocket = new ServerSocket(1234);
        System.out.println("服务端启动,等待连接");
        while (true) {
            //accept阻塞,serverSocket等待客户端的连接
            Socket accept = serverSocket.accept();
            System.out.println(Thread.currentThread().getName() + ":来自客户端的连接:" + accept.getRemoteSocketAddress());
            new Thread(new Runnable() {//实际应用的时候替换成线程池
                @Override
                public void run() {
                    try {
                        InputStream inputStream = accept.getInputStream();
                        Scanner scanner = new Scanner(inputStream);
                        //针对一个客户端socket连接进行IO操作
                        while (true) {
                            String line = scanner.nextLine();//阻塞,一直等待数据进来
                            if ("q".equals(line)) {
                                break;
                            }
                            System.out.println(Thread.currentThread().getName() +"收到数据:" + line);
                            accept.getOutputStream().write("I am server ask:".getBytes());
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}
  •  非阻塞式IO模型  NIO,Non-Blocking IO  

模型如图,应用进程向内核发出读取文件指令之后,内核就向磁盘发送指令准备数据去了,而应用进程不会阻塞等待去干其他事情了,过一会儿问问数据准备好了没,如果内核数据还没准备好,应用进程就继续干其他事情,一会儿一问数据好了没,这段时间中数据会从磁盘复制到内核空间,复制完成之后,待到应用进程再次询问的时候,内核就告诉它数据已经准备好了,在这个时候数据其实还是没有真正的进入到应用空间,此时应用进程又进入阻塞,等待数据从内核空间复制到应用空间,只不过这个阶段要比前一个阶段时间要短的多。总结就是第一个阶段不阻塞,第二个阶段阻塞

非阻塞式IO在Java中,jdk1.4以后有了一个java.nio.* 的包,Java中的nio实现其实是基于非阻塞式IO和多路复用IO模型实现的

  • IO复用模型

由于并不是所有的socket连接都要立马执行任务,IO复用模型,把所有的客户端连接都暂存到一个Map集合中,然后不断的循环遍历看哪些socket需要执行任务,就把它从map集合中获取处理进行处理

 

代码3:

public class NioMain {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置为非阻塞方式
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(1234));
        System.out.println("NIO 服务端开启成功。。。");

        Selector selector = Selector.open();
        //注册监听的事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //缓存区
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while (true){
            //循环监听客户端
            int select = selector.select();
            if (select==0){
                continue;
            }
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()){
                    ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel accept = channel.accept();
                    System.out.println("接收到客户端请求:"+accept.getRemoteAddress());
                    accept.configureBlocking(false);
                    //将客户端过来的channel设置成读取状态
                    accept.register(selector,SelectionKey.OP_READ);
                }
                if (selectionKey.isReadable()){
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    //通过channel将数据读取到缓存中
                    channel.read(buffer);
                    String str = new String(buffer.array());
                    buffer.clear();
                    System.out.println("接收到数据:"+str+"——来自:"+channel.getRemoteAddress());

                    //回写数据
                    channel.write(ByteBuffer.wrap("I am form Nio Server".getBytes()));
                }
                iterator.remove();
            }
        }
    }
}

 

 

 

推荐:https://www.cnblogs.com/Jack-Blog/p/11991240.html

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值