通俗易懂的JAVA BIO NIO AIO 原理白话文解释,区别,优缺点及代码使用案例

简介: 通俗易懂的JAVA BIO NIO AIO 原理白话文解释,区别,优缺点及代码使用案例

BIO 介绍与原理

BIO 全程Blocking I/O ,阻塞io, 是java最早的 网络io模型

基于基本ServerSocket ,Socket 实现,

SOCKET 所有的操作都是基于阻塞IO的,比如socket inpustream read方法

如果没有数据进来就会一直阻塞,除非新创建线程处理每一个socket

BIO 匹配我们饭店吃饭场景

我们先到第一个窗口,发现厨师还没做好,那么就一直等着,直到这到菜做好了,我们拿到这个菜,然会回到座位吃完了

然后又去第二个窗口,等待第二道菜做好,然后拿回座位吃完,以此循环下去

以上场景可以匹配ServerSocket 的accept 方法,以及Socket 的read write方法

一般解决方案是每一个socket 启用一个thread 处理,这会导致并发数很低

BIO 优缺点:

优点:代码通俗易懂,在低并发场景或者分布式场景依然应用很广

缺点: 所有流程都是阻塞模式,需要依赖创建多个thread 处理每个socket,并发量很低


BIO运行图:



5e44031affb4453cb40404435e9f9356.png

BIO 案例代码:

服务端最简单示例:

创建服务端有客户端连接后发送hello 字符串,并接受客户端reply 回复

/**
     * @description: demos of jdk8 java.net.ServerSocket class
     * 创建服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复
     *
     * */
    @Test
    public void testServer() throws IOException {
        ServerSocket serverSocket = new ServerSocket(8679);
        while(true){
            Socket socket = serverSocket.accept();
            socket.getOutputStream().write("hello".getBytes());
            try{
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                byte[] bytes = new byte[1024];
                int read = 0;
                while ((read =socket.getInputStream().read(bytes))>0){
                    byteBuffer.put(bytes,0,read);
                    byteBuffer.flip();
                    String rs = new String(byteBuffer.array());
                    System.out.println(rs);
                    byteBuffer.clear();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(!socket.isClosed()) {
                    try {
                        socket.close();
                    }catch (Exception e1){
                        e1.printStackTrace();
                    }
                }
            }
        }
    }

客户端最简单示例:

创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复

 /**
     * @description: demos of jdk8 java.net.Socket class
     * 创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复
     */
    @Test
    public void testClient() throws IOException {
        Socket socket = new Socket("127.0.0.1", 8679);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byte[] bytes = new byte[1024];
        int read = 0;
        while ((read = socket.getInputStream().read(bytes)) > 0) {
            byteBuffer.put(bytes, 0, read);
            byteBuffer.flip();
            String rs = new String(byteBuffer.array());
            byteBuffer.clear();
            System.out.println(rs);
            socket.getOutputStream().write("reply".getBytes());
        }
    }

NIO 介绍与原理

NIO 全称 non-blocking I/O ,意思是非阻塞io

与上面BIO 对应的,是JAVA 后加入的nio 包内的网络io模型

核心类是Selector ,    ServerSocketChannel,SocketChannel

NIO 匹配我们饭店吃饭场景

同样是进了一个饭店,饭店有几个窗口,每个窗口做不同的菜,比如宫保鸡丁,油闷大虾,醋溜鱼等等

我们这次选择另一种方式等菜,我们不断的在每个窗口走来走去,而不是一直等在某个窗口

每一轮查看就把所有做好的菜一起打包带走,拿回来放到桌上吃完,然后在去窗口再继续一轮轮循环往复

这样做对程序来说的好处就是一直只有一个线程在不断循环监听某些事件就绪了也就是Selector的select()方法,相当于上面的查看哪些菜好了

NIO 优缺点:

优点:这样做极大的压缩了需要使用的线程,极大的提高了并发量

确定:在高并发场景下会有阻塞问题,因为在处理某个任务的时候其他事件的处理需要等待

NIO运行图:

NIO 案例代码:

NIO 服务端最简单示例:

创建服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复

 /**
     * @description: demos of jdk8 java.nio.ServerSocketChannel class
     * java.nio.ServerSocketChannel 作用:ServerSocketChannel 是 socket nio server 服务端
     * ServerSocketChannel 例子:
     * nio 服务端简单demo,流程如下
     * 1. 服务端创建并注册OP_ACCEPT事件到selector 中
     * 2. 循环获取selector中事件
     * 2.1 如果是OP_ACCEPT 事件,则获取到对应连接的客户端socketChannel
     * 2.2 将socketChannel 注册读事件OP_READ到selector 中
     * 2.3 如果是OP_READ ,则读取信息
     */
    @Test
    public void testServerSocketChannel() throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(8970));
        ssc.configureBlocking(false);
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (true) {
            selector.select();
            Iterator<SelectionKey> skIterator = selector.selectedKeys().iterator();
            while (skIterator.hasNext()) {
                SelectionKey sk = skIterator.next();
                try {
                    if (sk.isValid() && sk.isAcceptable()) {
                        ServerSocketChannel assc = (ServerSocketChannel) sk.channel();
                        SocketChannel asc = assc.accept();
                        asc.configureBlocking(false);
                        asc.register(selector, SelectionKey.OP_READ);
                        byteBuffer.clear();
                        byteBuffer.put("hello".getBytes());
                        byteBuffer.flip();
                        asc.write(byteBuffer);
                    }
                    if (sk.isValid() && sk.isReadable()) {
                        SocketChannel sc = (SocketChannel) sk.channel();
                        byteBuffer.clear();
                        sc.read(byteBuffer);
                        byteBuffer.flip();
                        String rs = new String(byteBuffer.array());
                        System.out.println(rs);
                    }
                    skIterator.remove();
                } catch (Exception e) {
                    //e.printStackTrace();
                    sk.cancel();
                    sk.channel().close();
                }
            }
        }
    }

客户端最简单示例:

创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复

/**
     * @description: demos of jdk8 java.nio.ServerChannel class
     * java.nio.ServerChannel 作用:ServerChannel 是 客户端
     * ServerChannel 例子:
     */
    @Test
    public void testServerChannel() throws IOException {
        SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8970));
        sc.configureBlocking(false);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (true) {
            byteBuffer.clear();
            while (sc.read(byteBuffer) > 0) {
                byteBuffer.flip();
                String rs = new String(byteBuffer.array());
                System.out.println(rs);
                byteBuffer.clear();
                byteBuffer.put("reply".getBytes());
                byteBuffer.flip();
                sc.write(byteBuffer);
            }
        }
    }

AIO 介绍与原理

AIO 全称 Asynchronous I/O ,意思是异步io,属于完全的非阻塞io

也是NIO 包内的异步网络io模型 ,与上面NIO 相比,去掉了循环监听事件并收集取回遍历的过程核心类是 AsynchronousServerSocketChannel AsynchronousSocketChannel CompletionHandler

AIO 匹配我们饭店吃饭场景

我也又进入了这家饭店,饭店有几个窗口,每个窗口做不同的菜,比如宫保鸡丁,油闷大虾,醋溜鱼等等这次我们换了一种更方便的方式,我们不在每个窗口前走来走去看哪个菜好了

而是告诉每个窗口放菜的人,我在某某号桌,菜好了直接送到我桌子上就行,这样就完全避免了任何阻塞

无论在任何场景 accept ,read ,write 等都是注册一个回调函数,然后这些事件发生了自然就会回调到这些函数

AIO 优缺点:

优点:完全异步io,没有阻塞问题, 最大限度的提高并发量

缺点:需要计算机操作系统底层支持事件注册回调函数,目前来说大多数linux系统都支持注册回调,windows以及其他一些系统部分不支持,不支持的时候默认会用nio实现

AIO运行图:

AIO 案例代码:

服务端最简单示例:

创建AIO 服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复

 /**
     * @description: demos of jdk8 java.nio.AsynchronousServerSocketChannel CompletionHandler class
     * 创建AIO 服务端,有客户端连接后发送hello 字符串,并接受客户端reply 回复
     */
    @Test
    public void testAIOServer() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8097));
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel asc, Object attachment) {
                byteBuffer.clear();
                byteBuffer.put("hello".getBytes());
                byteBuffer.flip();
                asc.write(byteBuffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        byteBuffer.clear();
                    }
                    @Override
                    public void failed(Throwable exc, Object attachment) {
                    }
                });
                asc.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        byteBuffer.flip();
                        String rs = new String(byteBuffer.array());
                        System.out.println(rs);
                    }
                    @Override
                    public void failed(Throwable exc, Object attachment) {
                    }
                });
            }
            @Override
            public void failed(Throwable exc, Object attachment) {
            }
        });
        while(true) {}
    }

客户端最简单示例:

创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复

/**
     * @description: demos of jdk8 java.nio.AsynchronousSocketChannel CompletionHandler class
     * 创建客户端,连接到服务端后接收到hello 字符串,并接向服务端发送reply 字符串回复
     */
    @Test
    public void testAIOClient() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();
        asynchronousSocketChannel.connect(new InetSocketAddress("127.0.0.1", 8097), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                byteBuffer.clear();
                asynchronousSocketChannel.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        byteBuffer.flip();
                        String rs = new String(byteBuffer.array());
                        System.out.println(rs);
                        byteBuffer.clear();
                        byteBuffer.put("relpy".getBytes());
                        byteBuffer.flip();
                        asynchronousSocketChannel.write(byteBuffer);
                    }
                    @Override
                    public void failed(Throwable exc, Object attachment) {
                    }
                });
            }
            @Override
            public void failed(Throwable exc, Object attachment) {
            }
        });
        while(true) {}
    }



相关文章
|
2天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
|
19天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
53 3
|
19天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
21天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
40 2
|
22天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
14 2
|
27天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
5天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
13天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
4天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
4天前
|
Java 开发者
Java多线程编程的艺术与实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的技术文档,本文以实战为导向,通过生动的实例和详尽的代码解析,引领读者领略多线程编程的魅力,掌握其在提升应用性能、优化资源利用方面的关键作用。无论你是Java初学者还是有一定经验的开发者,本文都将为你打开多线程编程的新视角。 ####
下一篇
无影云桌面