初识Java中的NIO

1.概述

  Java NIO 全称java non-blocking IO ,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出新特性,被统称为 NIO(即 New IO),是同步非阻塞的。NIO采用内存映射文件的方式来处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件。NIO与原来的IO有同样的作用,但是使用的方式完全不同, NIO支持面向缓冲区的、基于通道的IO操作。 NIO将以更加高效的方式进行文件的读写操作。

2.NIO 三大核心原理示意图

  NIO 有三大核心部分:Channel(通道)Buffer(缓冲区)Selector(选择器)。NIO是面向缓冲区编程。数据读取到一个buffer中(缓冲区),需要时可在缓冲区内前后移动,增加了处理过程中的灵活性,使用它可提供非阻塞式的伸缩性网络。对于非阻塞式的理解:通俗来说就是一个线程可以处理多个操作
在这里插入图片描述

  • 每个 channel 都会对应一个 Buffer;
  • Selector 对应一个线程, 一个线程对应多个 channel(连接);
  • 每个 channel 都注册到 Selector选择器上;
  • Selector不断轮询查看Channel上的事件, 根据不同的事件完成不同的操作;
  • Buffer 就是一个内存块 , 底层是一个数组,NIO的Buffer是可以读也是可以写的,channel是双向的。

2.1 缓冲区Buffer

  缓冲区实际上是一个容器对象,底层是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。具体看下面这张图就理解了:

在这里插入图片描述
上图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入Buffer中,然后将Buffer中的数据写入通道。服务端这边接收数据必须通过Channel将数据读入到Buffer中,然后再从Buffer中取出数据来处理。

  在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer,对于Java中的基本类型,基本都有一个具体Buffer类型与之相对应,它们之间的继承关系如下图所示:
在这里插入图片描述

注:可以看到除了Boolean 类型外,其它都有对应的Buffer。

Buffer四个成员变量的说明

private int mark = -1;
private int position = 0;
private int limit;
private int capacity;

一般来说,四个属性的关系应该属于:

​ 0 <= mark <= position <= limit <= capacity

属性说明
capacity容量,即可以容纳的最大数据量;在缓冲区创建时设置并且不能改变
limit上限,缓冲区中当前的数据量
position位置,缓冲区中下一个要被读或写的元素的索引
mark调用mark()方法来设置mark=position,再调用reset()可以让position恢复到mark标记的位置,即position=mark

在这里插入图片描述

由于缓存区是读写共存,所以不同的模式下,这两个变量的值也具有不同的意义。

写模式下,所谓写模式就是将缓存区中的内容写入通道(buffer–>channel)。position 代表下一个字节应该被写出去的字节在缓存区中的位置,limit 表示最后一个待写字节在缓存区的位置。

读模式下,所谓读模式就是从通道读取数据到缓存区(channel–>buffers)。position 代表下一个读出来的字节应当存储在缓存区的位置,limit 等于 capacity。

2.2 通道channel

  Channel和传统IO中的Stream很相似。虽然相似,但是有很大的区别,主要区别为:通道是双向的,通过一个Channel既可以读,也可以写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如:InputStream只能进行读取操作,OutputStream只能进行写操作。但是通道和流一样都是需要基于物理文件的,而每个流或者通道都通过文件指针操作文件,这里说的「通道是双向的」也是有前提的,那就是通道基于随机访问文件『RandomAccessFile』的可读可写文件指针。

  通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

比喻:通常把IO比喻成为水流,管道就是水流的通道;把NIO比喻为火车的轨道,然后缓冲区就是上面的火车。
在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。它们之间的继承关系如下图所示:
在这里插入图片描述

  Channel(通道)表示到实体如硬件设备、文件、网络套接字或可以执行一个或多个不同I/O操作的程序组件的开放的连接。所有的Channel都不是通过构造器创建的,而是通过传统的节点InputStream、OutputStream的getChannel方法来返回响应的Channel。

  Channel中最常用的三个类方法就是map、read和write,其中map方法用于将Channel对应的部分或全部数据映射成ByteBuffer,而read或write方法有一系列的重载形式,这些方法用于从Buffer中读取数据或向Buffer中写入数据。

2.3 选择器Selector

  Selector类是NIO的核心类,**Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。**这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。

  与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。

  Selector选择器可以理解为一个IO事件的监听与查询器,通过选择器,一个线程可以查询多个通道的IO事件的就绪状态。

什么是IO事件?

表示通道某种IO操作已经就绪或者说已经做好了准备。

例如:如果一个新Channel连接建立成功,就会在Server Socket Channel上发生一个IO事件,代表一个新连接一个准备好,这个IO事件叫做“接收就绪”事件。

NIO定义了四个事件:SelectionKey.OP_ACCEPT、SelectionKey.OP_CONNECT、SelectionKey.OP_READ、SelectionKey.OP_WRITE

3.使用案例

3.1服务端

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) throws Exception {
        //创建Selector对象,管理多个channel
        Selector selector = Selector.open();
        //创建ServerSocketChannel对象(绑定主机名和端口号)
        ServerSocketChannel ssc = ServerSocketChannel.open().bind(new InetSocketAddress("localhost", 8080));
        //设置服务为非阻塞(必须配置,否则报错)
        ssc.configureBlocking(false);
        // 通道可支持的操作:支持新的连接 监听ACCEPT事件
        // ServerSocketChannel仅支持接受新连接,因此此方法返回SelectionKey.OP_ACCEPT 。
        int ops = ssc.validOps();
        //将通道注册到selector 等待连接
        SelectionKey selectKy = ssc.register(selector, ops, null);
        for (; ; ) {// 无条件的循环
            // 检测当前选择器注册通道是否有就绪事件,如果没有就阻塞,有事件,线程才会恢复运行
            int noOfKeys = selector.select();
            if (noOfKeys <= 0) {
                continue;
            }
            // 获取就绪的事件,即为选择器键集合
            Set selectedKeys = selector.selectedKeys();
            Iterator itr = selectedKeys.iterator();
            while (itr.hasNext()) {
                SelectionKey ky = (SelectionKey) itr.next();
                //选择键事件为接收就绪事件
                if (ky.isAcceptable()) {
                    //获取客户端连接通道
                    SocketChannel client = ssc.accept();
                    //配置客户端为非阻塞
                    client.configureBlocking(false);
                    //重点关注:READ事件
                    client.register(selector, SelectionKey.OP_READ);
                    // 选择键为READ事件
                } else if (ky.isReadable()) {
                    //获取客户端SocketChannel通道
                    SocketChannel client = (SocketChannel) ky.channel();
                    //设置缓冲区大小
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    //客户端读取缓冲区数据
                    client.read(buffer);
                    String output = new String(buffer.array()).trim();
                    System.out.println("接收客户端信息: " + output);
                    ByteBuffer buffer1 = ByteBuffer.wrap(("服务端时间戳:"+System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));
                    //将数据写回缓冲区
                    client.write(buffer1);
                }
                itr.remove();// 将选择键清空,防止下次循环时被重复处理
            }
        }
    }
}

3.2 客户端

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

public class Client {
    public static void main(String[] args) throws Exception {
        // 创建InetSocketAddress对象,绑定主机名和端口号
        InetSocketAddress hA = new InetSocketAddress("localhost", 8080);
        //获取客户端SocketChannel通道
        SocketChannel client = SocketChannel.open(hA);
        System.out.println("The Client is sending messages to server...");
        for (; ; ) {
            //实例化缓冲区对象
            ByteBuffer buffer = ByteBuffer.wrap(("客户端时间戳:" + System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));
            //数据写入缓冲区
            client.write(buffer);
            //清空缓冲区
            buffer.clear();
            //设置新缓冲区的大小
            ByteBuffer buffer1 = ByteBuffer.allocate(256);
            //读取新缓冲区数据
            client.read(buffer1);
            //打印结果到控制台
            System.out.println("接收服务器消息:" + new String(buffer1.array(), StandardCharsets.UTF_8).trim());
            TimeUnit.SECONDS.sleep(3);
        }

    }
}

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

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

相关文章

电池UN38.3测试电池模组蓄电池检测报告出具

UN38.3运输报告规定了以下类型的电池&#xff1a; 1. 锂金属电池&#xff1a;包括锂金属氧化物电池&#xff08;如锂锰电池、锂铁电池、锂钴电池&#xff09;&#xff0c;锂-硫电池等。 2. 锂离子电池&#xff1a;包括锂聚合物电池、锂离子聚合物电池、锂离子含水电池等。 3.…

软考-系统集成项目管理中级-新一代信息技术

本章历年考题分值统计 本章重点常考知识点汇总清单(掌握部分可直接理解记忆) 本章历年考题及答案解析 32、2019 年上半年第 23 题 云计算通过网络提供可动态伸缩的廉价计算能力&#xff0c;(23)不属于云计算的特点。 A.虚拟化 B.高可扩展性 C.按需服务 D.优化本地存储 【参考…

记录一个C语言基础错误——scanf()输入!

今天犯了一个很傻的问题&#xff0c;记录一下。 Lint’Code 题目&#xff1a; 错误代码 #include <stdio.h>int function(int a, int b, int c, int x, int y) {// Write your code hereprintf("In function: %d\n", x y);x - y;return (a * (x y) * (x y…

深圳MES系统服务商

盈致MES系统是一款专业的制造执行系统&#xff0c;专注于为企业提供全面的生产管理解决方案。该系统涵盖了制造数据管理、计划排程管理、生产调度管理、库存管理、质量管理等功能模块&#xff0c;能够帮助企业实现生产过程的数字化、智能化和精益化。 盈致MES系统具有以下特点和…

openharmony launcher 调研笔记(01)数据初始化

最近在看launcher&#xff0c;把自己调研的点做个笔记&#xff0c;持续修改更新中&#xff0c;个人笔记酌情参考。 初始化MainAbility ● common 等 包以 三方库形式 被引入使用 在每个包中的oh-package.json5 文件有配置 { "devDependencies": {}, "n…

宏的使用(C语言详解)

在写一个代码生成可执行文件的过程需要经过编译和链接&#xff0c;编译又要经过三部&#xff1a;预处理&#xff0c;编译&#xff0c;汇编。 #define定义的变量和宏就是在预处理阶段会处理的。 一个简单的宏定义&#xff1a; #include<stdio.h>; #define Max(a,b) a>…

如何利用义乌购API实现用户个性化推荐及商品详情 API 返回值说明

用户个性化推荐 利用义乌购API实现用户个性化推荐是一个涉及多个步骤的过程&#xff0c;主要包括数据收集、用户画像构建、推荐算法选择与实施以及推荐结果的展示与反馈。以下是一个大致的流程和步骤说明&#xff1a; 一、数据收集&#xff1a; 1.用户行为数据&#xff1a;收…

如何理解单片机 pwm 控制的基本原理?

单片机PWM&#xff08;脉宽调制&#xff09;控制的基本原理&#xff0c;简而言之&#xff0c;就是通过改变脉冲信号的宽度&#xff08;占空比&#xff09;来控制模拟电路。这涉及到单片机生成一系列脉冲信号&#xff0c;每个脉冲信号的高电平持续时间和整个周期的比值&#xff…

桌面便签电脑版哪个好?好用便签是哪款

在快节奏的现代生活中&#xff0c;桌面便签软件成为了我们不可或缺的助手。它们轻便、灵活&#xff0c;能够随时记录重要事项&#xff0c;提醒我们按时完成各项任务。面对市面上众多的便签软件&#xff0c;选择一款既实用又好用的便签显得尤为重要。经过深入体验&#xff0c;我…

C++ | Leetcode C++题解之第12题整数转罗马数字

题目&#xff1a; 题解&#xff1a; const string thousands[] {"", "M", "MM", "MMM"}; const string hundreds[] {"", "C", "CC", "CCC", "CD", "D", "DC&qu…

【Angular性能优化】项目8版本加载速度缓慢、白屏时间、首页渲染性能优化方案

前言 随着业务的代码一点点增加,加上Angular的项目本身就比 vue、react 的重一些,随之而来的启动速度,更改文件后编译速度,以及打包速度也会变慢,于是乎想着优化下我们的项目。 本文章主要说的是 : 打包Angular项目的一些配置,性能优化方面的方案打包后,用户进入页面…

vue vue3 手写 动态加载组件

效果展示 一、需求背景&#xff1a; # vue3 项目涉及很多图表加载、表格加载 #考虑手写一个动态加载组件 二、实现思路 通过一个加载状态变量&#xff0c;通过v-if判断&#xff0c;加载状态的变量等于哪一个&#xff0c;动态加载组件内部就显示的哪一块组件。 三、实现效果…

Coursera上托福专项课程03:TOEFL Test-Taking Strategies 学习笔记(完结)

TOEFL Preparation Specialization Specialization Certificate TOEFL Test-Taking Strategies Course Certificate 本文是学习 TOEFL Test-Taking Strategies 这门课的学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 文章目录 TOEFL Preparation SpecializationTOEF…

《web应用技术》第二次课后练习

练习目的&#xff1a; 1、form表单值的获取 2、mysql数据库及表的建立&#xff08;参见视频&#xff09; 3、maven项目的建立&#xff08;参见视频&#xff09; 4、使用jdbc进行数据库的增删改查操作。&#xff08;参见源代码&#xff09; 具体如下&#xff1a; 1、继续理…

以Kotti项目为例使用pytest测试项目

在维护和构建大型项目时&#xff0c;单独一个一个手工测试代码已经不适用了&#xff0c;这时候就要用专门的测试框架进行测试。让我们以Kotti项目为例&#xff0c;用pytest这个测试框架进行实践测试吧。 使用python3.10 Ubuntu 系统 准备工作 下载和安装kotti库 pip install…

并查集python实现及题目练习

文章目录 1. 并查集概念1.1 理解并查集&#xff1a;简介与应用场景1.2 Python 实现并查集及优化策略1.3 扁平化栈实现1.4 分析并查集的时间复杂度 2. 情侣牵手3. 相似字符串4. 岛屿数量 如果想了解并查集基础推荐去看左程云大神的算法讲解&#xff0c;非常不错&#xff0c;b站和…

【一】学习TDengine-总结新技术学习的思考

学习TDengine-总结新技术学习的思考 概要 因业务场景需要我们开始接触时序数据库&#xff0c;于是开始根据以往的学习经验着手熟悉这一项新技术&#xff0c;学习也是一种技能&#xff0c;成功的人越容易成功&#xff0c;因为他们掌握了一套成功的方法&#xff0c;这里提到学习经…

【LeetCode热题100】74. 搜索二维矩阵(二分)

一.题目要求 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;…

清明寄哀思,VR云祭扫沉浸式缅怀先烈

只要拿出手机扫一扫&#xff0c;就能通过VR全景&#xff0c;沉浸式走进烈士陵园、纪念场馆&#xff0c;随场景进行同步参观&#xff0c;进行“云祭扫”。这种“云祭扫”活动一经推出就受到了广大群众的追捧&#xff0c;VR全景云祭扫以一种全新、绿色、安全的理念&#xff0c;通…

别让.[[hashtreep@waifu.club]].svh勒索病毒盯上你:一份实用的科普与防范经验

引言&#xff1a; 在数字化浪潮席卷全球的今天&#xff0c;我们享受着信息技术带来的便捷与高效。然而&#xff0c;随着我们对网络的依赖越来越深&#xff0c;网络安全问题也日益凸显。其中&#xff0c;.[[hashtreepwaifu.club]].svh勒索病毒就是一种让人闻之色变的网络威胁。它…