JAVA NIO简解

news2025/6/20 20:55:04

1. 了解NIO

在这里插入图片描述

1.1.什么是NIO?

  1. Java nio是Java的一个新的输入输出(NewInput/Output)API,它提供了一些高效的数据处理方式,如缓冲区(buffers)、字符集(charsets)、通道(channels)和选择器(selectors)。
  2. Java NIO可以实现非阻塞式的多路复用输入输出,提高了程序的性能和可扩展性。Java nio是在Java 1.4版本中引入的,后来在Java7中又增加了NIO2,增加了一些新的特性,如文件系统、异步通道和文件监视等。Java NIO的主要类都在java NIO包和其子包中定义。

1.1.NIO和传统IO的区别

    1. JAVA IO是面向流的,JAVA nio是面向缓冲区的。面向流的意思是数据是一个字节一个字节地传输的,面向缓冲区的意思是数据是以块为单位传输的。
    1. JAVA IO是阻塞式的,JAVA nio是非阻塞式的。阻塞式的意思是当一个线程进行读写操作时,它会一直等待直到操作完成,而非阻塞式的意思是当一个线程进行读写操作时,它可以同时进行其他操作,当读写操作完成时会通知线程。
    1. JAVA IO主要有三种类型:字节流、字符流和对象流,JAVA nio主要有三种类型:缓冲区、通道和选择器。缓冲区是用于存储数据的容器,通道是用于连接IO设备的连接,选择器是用于管理多个通道的工具。
    1. JAVA IO主要使用装饰者模式来实现各种功能,JAVA nio主要使用适配器模式来实现各种功能。装饰者模式是通过包装原有的对象来增加新的功能,适配器模式是通过转换原有的对象来适应新的接口。

2.简单体验

ByteBuffer buf = ByteBuffer.allocateDirect(1024) 是在直接内存中的buff,速度更快,零地址转换

2.1.通道 – 读写数据

1.简单读写

//创建一个文件通道
FileChannel fileChannel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE);

//创建一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);

//从文件通道读取数据到缓冲区
int bytesRead = fileChannel.read(buffer);

//切换缓冲区到读模式
buffer.flip();

//从缓冲区读取数据并打印
while (buffer.hasRemaining()) {
    System.out.print((char) buffer.get());
}

//清空缓冲区
buffer.clear();

//向缓冲区写入数据
buffer.put("Hello, NIO!".getBytes());

//切换缓冲区到写模式
buffer.flip();

//从缓冲区写入数据到文件通道
int bytesWritten = fileChannel.write(buffer);

//关闭文件通道
fileChannel.close();

2.发散聚集

Nio的发散聚集是指使用一个通道分别读写多个缓冲区或者使用多个通道分别读写一个缓冲区的操作。Nio的发散聚集的代码操作如下

//创建一个随机访问文件
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");

//获取文件通道
FileChannel fileChannel = file.getChannel();

//创建两个缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(10);
ByteBuffer buffer2 = ByteBuffer.allocate(20);

//将两个缓冲区放入一个数组
ByteBuffer[] buffers = {buffer1, buffer2};

//从文件通道读取数据到两个缓冲区,这是发散操作
fileChannel.read(buffers);

//切换缓冲区到读模式
buffer1.flip();
buffer2.flip();

//从两个缓冲区读取数据并打印
while (buffer1.hasRemaining()) {
    System.out.print((char) buffer1.get());
}
System.out.println();
while (buffer2.hasRemaining()) {
    System.out.print((char) buffer2.get());
}
System.out.println();

//清空缓冲区
buffer1.clear();
buffer2.clear();

//向两个缓冲区写入数据
buffer1.put("Hello".getBytes());
buffer2.put("World".getBytes());

//切换缓冲区到写模式
buffer1.flip();
buffer2.flip();

//将两个缓冲区的数据写入到文件通道,这是聚集操作
fileChannel.write(buffers);

//关闭文件通道和随机访问文件
fileChannel.close();
file.close();

3.FileChannel内存映射

FileChannel内存映射文件是指将文件的一部分或全部映射到直接内存中,这样可以提高文件的访问效率,避免了数据在操作系统内存和JVM内存之间的拷贝123。FileChannel内存映射文件的代码操作如下:

//创建一个随机访问文件
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");

//获取文件通道
FileChannel fileChannel = file.getChannel();

//将文件的一部分映射到直接内存中,返回一个MappedByteBuffer对象
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());

//读取或修改直接内存中的数据
byte[] data = new byte[mappedByteBuffer.limit()];
mappedByteBuffer.get(data);
System.out.println(new String(data));
mappedByteBuffer.put(0, (byte) 'H');

//关闭文件通道和随机访问文件
fileChannel.close();
file.close();

2.2.管道(Pipe)–线程通信

java NIO
管道是两个线程之间的单向数据连接,有一个source通道和一个sink通道,数据会被写到sink通道,从source通道读取

//创建管道
Pipe pipe = Pipe.open();

//获取sink通道
Pipe.SinkChannel sinkChannel = pipe.sink();

//创建缓冲区并写入数据
ByteBuffer buffer = ByteBuffer.allocate(48);
buffer.put("Hello, world!".getBytes());
buffer.flip();

//将缓冲区的数据写入到sink通道
while (buffer.hasRemaining()) {
    sinkChannel.write(buffer);
}

//获取source通道
Pipe.SourceChannel sourceChannel = pipe.source();

//创建缓冲区并从source通道读取数据
buffer.clear();
int bytesRead = sourceChannel.read(buffer);

//打印缓冲区的数据
buffer.flip();
while (buffer.hasRemaining()) {
    System.out.print((char) buffer.get());
}

2.3.网络IO

  1. 服务端代码
//服务端代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioServer {
    public static void main(String[] args) throws IOException {
        //创建一个ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //绑定一个端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));

        //创建一个Selector对象
        Selector selector = Selector.open();

        //将ServerSocketChannel注册到Selector上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //在一个循环中调用Selector的select方法
        while (true) {
            //获取已经就绪的通道集合
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            //遍历就绪的通道集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //获取当前的SelectionKey
                SelectionKey selectionKey = iterator.next();

                //根据不同的事件类型进行处理
                if (selectionKey.isAcceptable()) {
                    //如果是OP_ACCEPT事件
                    //获取对应的SocketChannel对象
                    SocketChannel socketChannel = serverSocketChannel.accept();

                    //设置为非阻塞模式
                    socketChannel.configureBlocking(false);

                    //注册到Selector上,关注OP_READ事件
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    //如果是OP_READ事件
                    //获取对应的SocketChannel对象
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储读取到的数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    //从通道中读取数据到缓冲区
                    int len = socketChannel.read(buffer);

                    //判断是否读到了数据
                    if (len > 0) {
                        //切换缓冲区到读模式
                        buffer.flip();

                        //打印读取到的数据
                        System.out.println("Server received: " + new String(buffer.array(), 0, len));
                    }
                } else if (selectionKey.isWritable()) {
                    //如果是OP_WRITE事件
                    //获取对应的SocketChannel对象
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储要写入的数据
                    ByteBuffer buffer = ByteBuffer.wrap("Hello, client".getBytes());

                    //将缓冲区的数据写入到通道中
                    socketChannel.write(buffer);
                }

                //移除当前的SelectionKey,避免重复处理
                iterator.remove();
            }
        }
    }
}
  1. 客户端
//客户端代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioClient {
    public static void main(String[] args) throws IOException {
        //创建一个SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();

        //设置为非阻塞模式
        socketChannel.configureBlocking(false);

        //连接到服务器的地址和端口号
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));

        //创建一个Selector对象
        Selector selector = Selector.open();

        //注册到Selector上,关注OP_CONNECT事件
        socketChannel.register(selector, SelectionKey.OP_CONNECT);

        //在一个循环中调用Selector的select方法
        while (true) {
            //获取已经就绪的通道集合
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            //遍历就绪的通道集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //获取当前的SelectionKey
                SelectionKey selectionKey = iterator.next();

                //根据不同的事件类型进行处理
                if (selectionKey.isConnectable()) {
                    //如果是OP_CONNECT事件
                    //获取对应的SocketChannel对象
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    //如果连接已经完成
                    if (channel.finishConnect()) {
                        //注册到Selector上,关注OP_WRITE事件
                        channel.register(selector, SelectionKey.OP_WRITE);
                    }
                } else if (selectionKey.isWritable()) {
                    //如果是OP_WRITE事件
                    //获取对应的SocketChannel对象
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储要写入的数据
                    ByteBuffer buffer = ByteBuffer.wrap("Hello, server".getBytes());

                    //将缓冲区的数据写入到通道中
                    channel.write(buffer);

                    //注册到Selector上,关注OP_READ事件
                    channel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    //如果是OP_READ事件
                    //获取对应的SocketChannel对象
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储读取到的数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    //从通道中读取数据到缓冲区
                    int len = channel.read(buffer);

                    //判断是否读到了数据
                    if (len > 0) {
                        //切换缓冲区到读模式
                        buffer.flip();

                        //打印读取到的数据
                        System.out.println("Client received: " + new String(buffer.array(), 0, len));
                    }
                }

                //移除当前的SelectionKey,避免重复处理
                iterator.remove();
            }
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

No.036<软考>《(高项)备考大全》【第20章】项目集管理

【第20章】项目集管理1 考试相关2项目集合项目组合的区别2.1 项目组合、项目集、项目管理之间的关系&#xff1a;2.3 举例说三者的关系3 项目集管理3.1 项目集管理与项目管理区别3.2 项目集指导委员会3.3 项目集生命周期4 练习题参考答案1 考试相关 选择1分必考 案例概率低&am…

Ubuntu 23.04 新特性一览

Ubuntu 23.04 “Lunar Lobster” 计划于 2023 年 4 月 20 日星期四发布&#xff0c;这是 Ubuntu 桌面的第 38 个版本。作为一个短期版本&#xff0c;Ubuntu 23.04 共获得了 9 个月的持续更新、安全补丁和关键修复。 在正式发布之前&#xff0c;OMG! Ubuntu! 汇总整理了一些 Ub…

京东产发奔赴港股上市,分拆上市或成互联网大厂的共同选择?

‍数据智能产业创新服务媒体——聚焦数智 改变商业3月30日晚间&#xff0c;京东集团&#xff08;9618.HK&#xff09;相继发布2则公告称&#xff0c;拟分拆京东智能产发股份有限公司&#xff08;以下简称“京东产发”&#xff09;、京东工业股份有限公司&#xff08;以下简称“…

基于改进多目标灰狼优化算法的考虑V2G技术的风、光、荷、储微网多目标日前优化调度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

( “树” 之 DFS) 404. 左叶子之和 ——【Leetcode每日一题】

404. 左叶子之和 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24 示例 2: 输入: root [1]…

当ChatGPT续写《红楼梦》,能替代原著吗?

来源: 清华大学出版社 近段时间&#xff0c;人工智能聊天机器人ChatGPT火爆网络&#xff0c;“AI写作是否会让文字工作者被替代&#xff1f;”成为人们关注并持续讨论的话题。 闲聊、问答、解题、写代码、写诗、创作小说&#xff0c; 连续回答&#xff0c;不断纠错&#xff0c…

window下面安装phpstudy进行PHP开发

文章目录前言在这里插入图片描述一、安装步骤1.1 下载PHPStudy软件1.2 安装PHPStudy软件1.3 启动PHPStudy软件1.4 设置PHPStudy1.5 安装扩展1.6 启动服务总结前言 php的集成开发环境比较多&#xff0c;有wamp server&#xff0c;xmapp&#xff0c;phpstudy等。经过这些年的体验…

【李宏毅】深度学习——HW5-Machine Translation

Machine Translation 1. Goal 给定一段英文&#xff0c;翻译成繁体中文 2. Introduction 2.1 Dataset training dataset TED2020: TED talks with transcriptions translated by a global community of volunteers to more than 100 language.we will use (en, zh-tw) alig…

身临其境数字世界:探索VR全景元宇宙展厅

随着科技的不断发展&#xff0c;虚拟现实技术已经成为我们生活中的一部分。VR全景元宇宙展厅作为其中的一种形式&#xff0c;正越来越受欢迎。在这里&#xff0c;您可以探索未知的世界&#xff0c;体验全新的视觉和感官体验。 一、VR全景元宇宙展厅的概述 VR全景元宇宙展厅是一…

Kafka---kafka概述和kafka基础架构

kafka概述和kafka基础架构 文章目录kafka概述和kafka基础架构Kafka定义消息队列传统消息队列应用场景缓存/消峰解耦异步通信消息队列的两种模式点对点模式发布/订阅模式kafka基础架构producerConsumerConsumer Group&#xff08;CG&#xff09;BrokerTopicPartitionReplicaLead…

基于html+css的盒子内容旋转180度

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

如何学习python自动化测试,这是我见过最完整的教程了

目录 前言 一、 学习Python基础知识 二、 学习自动化测试框架 三、 学习Web自动化测试 四、 学习移动端自动化测试 五、 学习版本控制工具 六、 学习测试管理工具 七、 实践总结 前言 Python自动化测试是目前比较流行的一种自动化测试技术。它具有开发效率高、可扩展性…

《c++入门》-超级详细讲解

本文主要介绍c的一些入门知识&#xff0c;为后面打基础 文章目录前言1、C关键字(C98)2、命名空间2.1 命名空间定义2.2 命名空间使用1.指定命名空间2.全局展开&#xff08;一般情况&#xff0c;不建议全局展开&#xff09;3.部分展开3、C输入&输出4、缺省参数4.1 缺省参数定…

人工智能(Pytorch)搭建T5模型,真正跑通T5模型,用T5模型生成数字加减结果

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(Pytorch)搭建T5模型,真正跑通T5模型,用T5模型生成数字加减结果。T5&#xff08;Text-to-Text Transfer Transformer&#xff09;是一种由Google Brain团队在2019年提出的自然语言处理模型。T5模型基于Tran…

商办楼宇租赁过程中的风险与管控

在商办地产租赁市场持续高量供应、越来越多楼盘趋向同质化的背景下&#xff0c;商办地产经营需更懂得审时度势&#xff0c;在租赁经营过程中合理运用数字化管理识别、规避风险&#xff0c;针对有风险的经营及时调整管控&#xff0c;提升识别及防范风险的意识和能力&#xff0c;…

数据结构:链表oj下

21. 合并两个有序链表 CM11 链表分割 不加36行代码会造成死循环&#xff08;形成环&#xff09; OR36 链表的回文结构 找到中间节点&#xff0c;再把后面的逆置 走完一个链表while(tail) 找链表的最后一个节点while(tail->next) 160. 相交链表 找到AB链表的尾节点&#x…

Python 小型项目大全 6~10

六、凯撒密码 原文&#xff1a;http://inventwithpython.com/bigbookpython/project6.html 凯撒密码是朱利叶斯凯撒使用的一种古老的加密算法。它通过将字母在字母表中移动一定的位置来加密字母。我们称移位的长度为密钥。比如&#xff0c;如果密钥是 3&#xff0c;那么A变成D&…

Linux-初学者系列——篇幅2_系统命令界面

系统命令界面-目录一、命令行提示符1、提示符2、提示符组成3、提示符修改二、系统命令语法规范三、系统命令行常用快捷键1、常用快捷键2、移动光标快捷键3、剪切、粘贴、清楚快捷键4、系统管理控制快捷键5、重复执行命令快捷键上篇: Linux-初学者系列——篇幅1_文件管理命令 一…

Python 小型项目大全 36~40

三十六、沙漏 原文&#xff1a;http://inventwithpython.com/bigbookpython/project36.html 这个可视化程序有一个粗糙的物理引擎&#xff0c;模拟沙子通过沙漏的小孔落下。沙子堆积在沙漏的下半部分&#xff1b;然后把沙漏翻过来&#xff0c;重复这个过程。 运行示例 图 36-…

手写vuex4源码(八)插件机制实现

一、插件的使用 Vuex 不仅提供了全局状态管理能力&#xff0c;还进一步提供了插件机制&#xff0c;便于开发者对 Vuex 插件进行增强&#xff1b; Vuex 插件的使用方式&#xff1a;通过 Store 类提供的 plugin 数组进行 Vuex 插件注册&#xff1a; export default createStor…