详解Java中的BIO、NIO、AIO

1、 详解Java中的BIO、AIO、NIO

1.1、引言

IO流是Java中比较难理解的一个知识点,但是IO流在实际的开发场景中经常会使用到,比如Dubbo底层就是NIO进行通讯。本文将介绍Java发展过程中出现的三种IO:BIO、NIO以及AIO,重点介绍NIO。

1.2、什么是BIO

BIO即同步阻塞IO,实现模型为一个连接就需要一个线程去处理。这种方式简单来说就是当有客户端来请求服务器时,服务器就会开启一个线程去处理这个请求,即使这个请求不干任何事情,这个线程都一直处于阻塞状态。

BIO模型有很多缺点,最大的缺点就是资源的浪费。想象一下如果QQ使用BIO模型,当有一个人上线时就需要一个线程,即使这个人不聊天,这个线程也一直被占用,那再多的服务器资源都不管用。


 

代码演示:

package com.atguigu.bio;

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BIOServer {
    public static void main(String[] args) throws Exception {
        //线程池机制
        //思路
        //1. 创建一个线程池
        //2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法)
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("服务器启动了");
        while (true) {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" +      
            Thread.currentThread().getName());
            //监听,等待客户端连接
            System.out.println("等待连接....");
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");

            //就创建一个线程,与之通讯(单独写一个方法)
            newCachedThreadPool.execute(new Runnable() {
                public void run() { //我们重写
                    //可以和客户端通讯
                    handler(socket);
                }
            });
        }
    }

    //编写一个handler方法,和客户端通讯
    public static void handler(Socket socket) {
        try {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + 
            Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //通过socket 获取输入流
            InputStream inputStream = socket.getInputStream();
            //循环的读取客户端发送的数据
            while (true) {
                System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" +    
                Thread.currentThread().getName());
                System.out.println("read....");
               int read =  inputStream.read(bytes);
               if(read != -1) {
                   System.out.println(new String(bytes, 0, read
                   )); //输出客户端发送的数据
               } else {
                   break;
               }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭和client的连接");
            try {
                socket.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

演示
快捷键win+R打开cmd窗口,根据上面代码拼写6666端口
输入 telnet 127.0.0.1 6666
进入黑窗口

输入ctrl+]
向服务端发送文本
send hello world

服务端接收的

当有
多个cmd窗口时,则创建多一个线程与之对应
退出时,cmd输入
quit
服务端显示

2、什么是NIO

BIO是阻塞的,如果没有多线程,BIO就需要一直占用CPU,而NIO则是非阻塞IO,NIO在获取连接或者请求时,即使没有取得连接和数据,也不会阻塞程序。NIO的服务器实现模式为一个线程可以处理多个请求(连接)。
NIO有几个知识点需要掌握,Channel(通道),Buffer(缓冲区), Selector(多路复用选择器)。
Channel既可以用来进行读操作,又可以用来进行写操作。NIO中常用的Channel有FileChannel
、SocketChannel、ServerSocketChannel、DatagramChannel。

Buffer缓冲区用来发送和接受数据。

Selector 一般称为选择器或者多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。在javaNIO中使用Selector往往是将Channel注册到Selector中。

下面我通过代码的方式模拟javaNIO的运行流程。

2.1、NIO代码实践

首先贴上NIO的实践代码:NIO服务端详细的执行过程是这样的:创建一个ServerSocketChannel和Selector,然后将ServerSocketChannel注册到Selector上
Selector通过select方法去轮询监听channel事件,如果有客户端要连接时,监听到连接事件。
通过channel方法将socketchannel绑定到ServerSocketChannel上,绑定通过SelectorKey实现。
socketchannel注册到Selector上,关心读事件。
Selector通过select方法去轮询监听channel事件,当监听到有读事件时,ServerSocketChannel通过绑定的SelectorKey定位到具体的channel,读取里面的数据。

public class NioServer {
    public static void main(String[] args) throws IOException {
        //创建一个socket通道,并且设置为非阻塞的方式
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(9000));
        //创建一个selector选择器,把channel注册到selector选择器上
        Selector selector=Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true){
            System.out.println("等待事件发生");
            selector.select();
            System.out.println("有事件发生了");
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                handle(key);
            }
        }

    }

    private static void handle(SelectionKey key) throws IOException {
        if (key.isAcceptable()){
            System.out.println("连接事件发生");
            ServerSocketChannel serverSocketChannel= (ServerSocketChannel) key.channel();
            //创建客户端一侧的channel,并注册到selector上
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(key.selector(),SelectionKey.OP_READ);
        }else if (key.isReadable()){
            System.out.println("数据可读事件发生");
            SocketChannel socketChannel= (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = socketChannel.read(buffer);
            if (len!=-1){
                System.out.println("读取到客户端发送的数据:"+new String(buffer.array(),0,len));
            }
            //给客户端发送信息
            ByteBuffer wrap = ByteBuffer.wrap("hello world".getBytes());
            socketChannel.write(wrap);
            key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
            socketChannel.close();
        }
    }
}

客户端代码:NIO客户端代码的实现比BIO复杂很多,主要的区别在于,NIO的客户端也需要去轮询自己和服务端的连接情况。

客户端发送数据后,获取到了来自服务端的回复。

2.2、NIO总结

NIO通过一个Selector,负责监听各种IO事件的发生,然后交给后端的线程去处理。NIO相比与BIO而言,非阻塞体现在轮询处理上。BIO后端线程需要阻塞等待客户端写数据,如果客户端不写数据就一直处于阻塞状态。而NIO通过Selector进行轮询已注册的客户端,当有事件发生时才会交给后端去处理,后端线程不需要等待。

3、什么是AIO

AIO是在JDK1.7中推出的新的IO方式–异步非阻塞IO,也被称为NIO2.0,AIO在进行读写操作时,直接调用API的read和write方法即可,这两种均是异步的方法,且完成后会主动调用回调函数。简单来讲,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。

Java提供了四个异步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。

3.1、AIO代码实践

服务器端代码:AIO的创建方式和NIO类似,先创建通道,再绑定,再监听。只不过AIO中使用了异步的通道。

public class AIOServer {
    public static void main(String[] args) {
        try {
            //创建异步通道
            AsynchronousServerSocketChannel serverSocketChannel=AsynchronousServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8080));
            System.out.println("等待连接中");
            //在AIO中,accept有两个参数,
            // 第一个参数是一个泛型,可以用来控制想传递的对象
            // 第二个参数CompletionHandler,用来处理监听成功和失败的逻辑
            //  如此设置监听的原因是因为这里的监听是一个类似于递归的操作,每次监听成功后要开启下一个监听
            serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
                //请求成功处理逻辑
                @Override
                public void completed(AsynchronousSocketChannel result, Object attachment) {
                    System.out.println("连接成功,处理数据中");
                    //开启新的监听
                    serverSocketChannel.accept(null,this);
                    handledata(result);
                }
                @Override
                public void failed(Throwable exc, Object attachment) {
                    System.out.println("失败");
                }
            });
            try {
                TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void handledata(AsynchronousSocketChannel result) {
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        //通道的read方法也带有三个参数
        //1.目的地:处理客户端传递数据的中转缓存,可以不使用
        //2.处理客户端传递数据的对象
        //3.处理逻辑,也有成功和不成功的两个写法
        result.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                if (result>0){
                    attachment.flip();
                    byte[] array = attachment.array();
                    System.out.println(new String(array));
                }
            }
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.out.println("失败");
            }
        });
    }
}

客户端代码基本上没有太多差别,主要还是实现数据的发送功能

public class AIOClient {
    public static void main(String[] args) {
        try {
            AsynchronousSocketChannel socketChannel=AsynchronousSocketChannel.open();
            socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));
            Scanner scanner=new Scanner(System.in);
            String next = scanner.next();
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            byteBuffer.put(next.getBytes());
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

观察结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/888552.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

读数据工程之道:设计和构建健壮的数据系统03数据工程生命周期(上)

1. 数据工程生命周期 1.1. 数据领域正在经历新数据技术和实践的爆炸式增长&#xff0c;抽象程度和易用性不断提高 1.2. 由于技术抽象程度的增加&#xff0c;数据工程师将越来越多地成为数据生命周期工程师&#xff0c;根据数据生命周期管理的原则来进行思考和操作 1.3. 数据…

专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结

目录 搜索 vs 深度优先遍历 vs 深度优先搜索 vs 宽度优先遍历 vs 宽度优先搜索 vs 暴搜 1.深度优先遍历 vs 深度优先搜索(dfs) 2.宽度优先遍历 vs 宽度优先搜索(bfs) 2.关系图暴力枚举一遍所有的情况 3.拓展搜索问题全排列 决策树 1. 计算布尔⼆叉树的值&#xff08;medi…

yub‘s Algorithmic Adventures_Day7

环形链表 link&#xff1a;https://leetcode.cn/problems/linked-list-cycle-ii/description/ 思路分析 我只能说双指针yyds【刻板hh】 我们分两种情况来分析 起码在第二圈才会相遇 fast比slow多走环的整数倍 fast 走的步数是 slow 步数的 2 倍&#xff0c;即 f2s&#xff…

计算机的错误计算(一百一十七)

摘要 算式“(5^25*(1/25)^(1/5)*3^25(1/25)^(1/5)*5^25*3^(251/5)-(9/25)^(1/5)*3^25*5^25-(1/25)^(1/5)*3^25*5.0^25*(13^(1/5)-3^(2/5.0)))” 的准确值是0. 但是&#xff0c;Python 与 Excel 均输出了错误结果&#xff1a;一个含有15位整数&#xff0c;一个含有14位整数。 …

Python | Leetcode Python题解之第464题我能赢吗

题目&#xff1a; 题解&#xff1a; class Solution:def canIWin(self, maxChoosableInteger: int, desiredTotal: int) -> bool:cachedef dfs(usedNumbers: int, currentTotal: int) -> bool:for i in range(maxChoosableInteger):if (usedNumbers >> i) & 1…

初学者如何快速入门人工智能

一、引言 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;&#xff0c;作为当今科技领域极具前景与影响力的方向之一&#xff0c;吸引着众多人士投身其中。无论是对科技充满好奇的学生&#xff0c;还是意图拓展职业发展路径的职场人士&#xff0c…

网络知识_001_浏览器输入域名

文章目录 网络模型IP地址&#xff0c;子网掩码&#xff0c;网关&#xff0c;网络地址&#xff0c;广播地址&#xff0c;NAT转换浏览器输入域名到网页打开发生了什么DNS获取顺序 网络模型 模型协议工具报文添加信息作用应用层http&#xff0c;https&#xff0c;ftp&#xff0c;…

认识动态规划算法和实践(java)

前言 动态规划算法里面最有意思的一个东西之一。动态规划初学肯定会有一定晦涩难懂。如果我们去网上搜索&#xff0c;动态规划的资料&#xff0c;它一开始都是将很多的理论&#xff0c;导致会认为很难&#xff0c;但是这个东西实际上是有套路的。 动态规划的英语是Dynamic Pr…

Java爬虫技术:解锁1688商品搜索的新维度

Java爬虫技术简介 Java爬虫技术是指使用Java语言编写的程序&#xff0c;模拟浏览器行为&#xff0c;自动化地从互联网上获取信息。随着技术的发展&#xff0c;Java爬虫技术已经非常成熟&#xff0c;有多种框架和库可以使用&#xff0c;如Jsoup、HttpClient、WebMagic等。 1688…

【操作系统】引导(Boot)电脑的奇妙开机过程

&#x1f339;&#x1f60a;&#x1f339;博客主页&#xff1a;【Hello_shuoCSDN博客】 ✨操作系统详见 【操作系统专项】 ✨C语言知识详见&#xff1a;【C语言专项】 目录 什么是操作系统的引导&#xff1f; 操作系统的引导&#xff08;开机过程&#xff09; Windows操作系…

【2024最新】华为HCIE认证考试流程

HCIE是华为认证体系中最高级别的ICT技术认证&#xff0c;表示通过认证的人具有ICT领域专业知识和丰富实践经验。 HCIE认证方向&#xff1a;最高认证级别HCIE的技术方向有13个 下面以HCIE-Datacom为例给大家介绍一下&#xff1a; HCIE-Datacom认证考试流程&#xff1a; 1.笔试…

ecmascript标准

ECMAScript&#xff08;简称ES&#xff09;是由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff0c;European Computer Manufacturers Association&#xff09;制定的一种标准化的脚本程序设计语言。它是JavaScript的核心&#xff0c;定义了语言的语法、类型、语句、关…

Stable Diffusion最新版nowebui的api使用详解

最近在使用stable diffusion最新版的Stable Diffusion WebUI Forge进行api调用,下面来一步一步的进行展开吧!!! 1、下载lllyasviel/stable-diffusion-webui-forge GitHub - lllyasviel/stable-diffusion-webui-forgeContribute to lllyasviel/stable-diffusion-webui-for…

医院管理智能化:Spring Boot技术革新

3系统分析 3.1可行性分析 通过对本医院管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本医院管理系统采用JAVA作为开发语言&#xff0c;Spring Boot框…

C++——类和对象(二)

1. 类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。⼀个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0c;需要注意的是这6个中最重要的是前4个&#xff0c;最后两个取地址重载不…

探索杨辉三角形的奥秘:C#实现

文章目录 杨辉三角形简介特点 C#实现杨辉三角形环境准备代码实现代码解释1. 用户输入2. 输入验证3. 初始化第一行4. 生成杨辉三角形5. 打印杨辉三角形 结论 杨辉三角形&#xff0c;也被称为帕斯卡三角形&#xff0c;是一个历史悠久且充满数学魅力的数字模式。它不仅在数学领域有…

裸眼3D巨幕视频演示Pr城市广告显示屏样机模板

震撼大气超强视觉冲击力3D城市数字广告牌视频演示pr模板工程文件。 5个城市街景裸眼3D巨幕户外广告显示屏样机模板。每个场景提供2个不同的相机视图。 下载地址&#xff1a;https://prmuban.com/40595.html

泡沫背后:人工智能的虚幻与现实

人工智能的盛世与泡沫 现今&#xff0c;人工智能热潮席卷科技行业&#xff0c;投资者、创业者和用户都被其光环吸引。然而&#xff0c;深入探讨这种现象&#xff0c;人工智能的泡沫正在形成&#xff0c;乃至具备崩溃的潜质。我们看到的&#xff0c;无非是一场由资本推动的狂欢…

OCP迎来新版本,让OceanBase的运维管理更高效

近期&#xff0c;OceanBase的OCP发布了新版本&#xff0c;全面支持 OceanBase 内核 4.3.2 及更低版本。新版本针对基础运维、性能监控、运维配置、外部集成等多个方面实现了 20余项的优化及强化措施&#xff0c;增强产品的易用性和稳定性&#xff0c;从而帮助用户更加高效地管理…

第二期:第14节, beep 编程

首先是 硬件电路图&#xff1a; 然后是数据手册中 找到 相关的寄存器&#xff0c; 首先是 GPIO的复用寄存器&#xff0c; 然后是 PWM的寄存器。 首先是 关于GPIO的 复用 &#xff0c; 这里是 Tout&#xff0c; 也就是 定时器&#xff0c; pwm 通过 定时器 通过某种方式来控…