全网最清晰JAVA NIO,看一遍就会

这篇具有很好参考价值的文章主要介绍了全网最清晰JAVA NIO,看一遍就会。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

目录

1.概述

1.1.计算机的IO模型

1.2.NIO概述

2.buffer

2.1.概述

2.2.API

2.3.代码示例

3.channel

3.1.概述

3.2.API

3.2.1.读写

3.2.2.文件复制

3.2.3.阻塞与非阻塞

4.selector

4.1.概述

4.2.代码示例


1.概述

1.1.计算机的IO模型

在聊IO之前一定要了解计算机的IO模型,因为编程语言的所有和IO有关的API,本质上一定是对于计算机IO模型的抽象。

计算机的IO其实就是在内存中为各个IO设备分配了属于它的一块内存,向这块内存中进行读写即可完成IO。这块内存是位于计算机内存中的内核段中的。

一次完整的IO过程是:

1.CPU执行IO指令

2.将内核段中数据读到用户段

3.用户段中数据交给CPU

4.CPU的执行结果写回用户段

5.用户段中存的结果写回内核段

全网最清晰JAVA NIO,看一遍就会,JAVA SE,网络,java

 如果对计算机的内存分段、IO等相关概念不熟悉的同学,可以移步博主的另一篇文章,里面详细介绍了相关内容:

详解零拷贝__BugMan的博客-CSDN博客

1.2.NIO概述

NIO,non-blocking IO,从JDK1.4版本开始引入,其直观的特点就是非阻塞,深入一点来看的话,NIO推出之前的JAVA BIO只是对TCP进行了简单的封装,用户只能对进行简单的IO,而整个计算机底层,在内存中的IO过程是被封装成了黑盒的。NIO对计算机底层的IO过程做了抽象,开放出来了内存粒度的API,让使用者可以更加细粒度的从计算机内存的角度来控制IO。

如果不是很了解BIO的同学可以移步作者的另一篇文章,其中详细讲解了BIO:

JAVA BIO__BugMan的博客-CSDN博客

NIO有三大核心:

  1. channel
  2. buffer
  3. selector

1.buffer:

可以理解为用户段的内存的抽象。

2.channel:

可以理解为用户段和内核段IO区的连接的抽象,当然直接理解为内核段的IO区的抽象其实也可以。

3.selector:

NIO实现非阻塞式IO的核心,其可以基于事件监听的方式,选择准备好的channel,再去其中将数据读到buffer中,然后去操作buffer中的数据。这里要是有点晕,不要紧,后文在selector章节,会详细介绍。

2.buffer

2.1.概述

buffer,理解为用户段中一块内存的抽象即可。

全网最清晰JAVA NIO,看一遍就会,JAVA SE,网络,java

既然是一块内存,那么其本质上就是用来进行数据读写的一个容器,由java.nio包定义,顶级接口为Buffer,定义了一套API用来管理缓 冲区中的数据针对存储不同的数据类型,有不同的buffer:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

buffer具有以下几个基本属性:

  • 容量(capacity),buffer的大小,buffer创建后,容量不能更改。
  • 限制(limit) ,buffer可用的大小,limit之后的区域无法进行读写。
  • 位置(position) 表示接下来要读写的数据的所在位置。
  • 标记(mark) 标记一个位置的索引,调用reset()方法可以回到该位置上

图示:

全网最清晰JAVA NIO,看一遍就会,JAVA SE,网络,java

2.2.API

  • Buffer clear() 清空缓冲区并返回对缓冲区的引用
  • Buffer flip()为 将缓冲区的界限设置为当前位置,并将当前位置充值为 0 0
  • int capacity() 返回 Buffer 的 capacity 大小
  • boolean hasRemainingO) 判断缓冲区中是否还有元素
  • int Timit0 返回 Buffer 的界限(Timit) 的位置
  • Buffer limit(int n) 将设置缓冲区界限为 n,并返回一个具有新  imit 的缓冲区对象
  • Buffer mark() 对缓冲区设置标记
  • int position() 返回缓冲区的当前位置 position
  • Buffer position(int n) 将设置缓冲区的当前位置为 n ,并返回修改后的 Buffer 对象
  • int remaining() 返回 position 和  imit 之间的元素个数
  • Buffer reset() 将位置 position 转到以前设置的 mark 所在的位置
  • Buffer rewind() 将位置设为为 0, 取消设置的 mark
  • get() : 读取单个字节get(byte[] dst): 批量读取多个字节到 dst 中
  • get(int index): 读取指定索引位置的字节(不会移动 position)
  • put(byte b): 将给定单个字节写入缓冲区的当前位置
  • put(byte[] src): 将 src 中的字节写入缓冲区的当前位置
  • put(int index,byte b): 将指定字节写入缓冲区的索引位置(不会移动 position)

2.3.代码示例

由于buffer其实就是一块内存的抽象,是一个数据容器,所以核心其实就是put、get。

此处以byteBuffer为例,其它相同。

byte[] resources = "hello".getBytes();
//初始化
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.limit());
//写
byteBuffer.put(resources);
System.out.println(byteBuffer.capacity());
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.limit());
//读
//不开读模式,读不到任何数据
System.out.println(byteBuffer.get());
System.out.println(byteBuffer.get());
//开起读模式才能读到数据
byteBuffer.flip();
System.out.println(byteBuffer.get());
System.out.println(byteBuffer.get());

//读取全部
System.out.println(new String(byteBuffer.array()));

3.channel

3.1.概述

Channel,通道,由java.nio.channel包下定义,用来向buffer中读写数据,可以理解为内核段和用户段之间进行数据传输的一条逻辑通道,甚至可以直接理解为内核段中内存的一个抽象。

全网最清晰JAVA NIO,看一遍就会,JAVA SE,网络,java

通道具有以下特性:

  • 全双工,读写可以同时进行,即可以向缓冲区中写,又可以向缓冲区中读
  • 支持异步

Channel是一个顶级父接口,针对需要传输的数据格式的不同分为:

  • FileChannel 用于读取、写入、映射操作文件
  • DatagramChannel 用于通过UDP读写网络中的数据
  • SocketChannel 通过TCP读写网络中的数据,底层封装的Socket
  • ServerSocketChannel SocketChannel的升级版,可以自动监听新的TCP连接,每一条新连接创建一个SocketChannel。

可以从以下地方获取不同的通道:

  • FileInputStream
  • FileOutStream
  • RandomAccessFile
  • Socket ServerSocket

3.2.API

3.2.1.读写

用channel将buffer中的数据写出来:

       try {
            FileOutputStream fos = new FileOutputStream( "nio_channel/data01.txt");
            //获取file类型的channel
            FileChannel channel=fos.getChannel();
            //准备好要写出的内容
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            buffer.put("helloWorld!".getBytes());
            //将buffer切换成读模式
            buffer.flip();
            //写出
            channel.write(buffer);
            channel.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

用channel将数据读进buffer:

        try {
            FileInputStream is=new FileInputStream("data01.txt");
            FileChannel channel = is.getChannel();
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            //用channel将数据读到buffer中
            channel.read(buffer);
            System.out.println(new String(buffer.array()));
        } catch (IOException e) {
            e.printStackTrace();
        }

3.2.2.文件复制

在JAVA NIO中有两种方式可以实现文件的复制:

  1. 非零拷贝,即一个通道向buffer中写,另一个通道去buffer中读,数据要走用户段。
  2. 零拷贝,直接从磁盘的一个地方拷贝到磁盘的另一个地方,数据不用走用户段。

这里要是对零拷贝不了解的同学,可以移步博主的另一篇文章,对零拷贝进行了详细讲解:

详解零拷贝__BugMan的博客-CSDN博客

1.非零拷贝:

一个channel向buffer中写,另一个channel去buffer中读。

        try {
            File srcFile=new File("data01.txt");
            File targetFile=new File("data02.txt");
            FileInputStream fis=new FileInputStream(srcFile);
            FileOutputStream fos=new FileOutputStream(targetFile);

            FileChannel isChannel=fis.getChannel();
            FileChannel osChannel=fos.getChannel();

            ByteBuffer buffer=ByteBuffer.allocate(1024);
            while (true){
                //读数据
                int flag=isChannel.read(buffer);
                if(flag==-1){
                    break;
                }
                //读模式
                buffer.flip();
                //写数据
                osChannel.write(buffer);
                //清空buffer
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

2.零拷贝:

当使用Java NIO进行文件传输时,提供了两个底层使用零拷贝的API,一个是transferTo、一个是transferFrom。可以通过transferTo方法将数据从一个Channel传输到另一个Channel,也可以使用transferFrom方法将数据从一个Channel传输到另一个Channel。

// 定义源文件和目标文件路径
String sourceFilePath = "path/to/source/file.txt";
String targetFilePath = "path/to/target/file.txt";

// 创建源文件和目标文件的RandomAccessFile对象
try (RandomAccessFile sourceFile = new RandomAccessFile(sourceFilePath, "r");
RandomAccessFile targetFile = new RandomAccessFile(targetFilePath, "rw")) {

// 获取源文件和目标文件的FileChannel
FileChannel sourceChannel = sourceFile.getChannel();
FileChannel targetChannel = targetFile.getChannel();

// 使用transferTo()方法将数据从源文件传输到目标文件
// 从源文件的位置0开始,传输全部文件内容到目标文件
long transferredBytes = sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
System.out.println("文件传输成功,传输了 " + transferredBytes + " 字节数据。");
// 使用transferFrom()方法将数据从目标文件传输回源文件
// 从目标文件的位置0开始,传输全部文件内容回源文件
transferredBytes = targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
System.out.println("数据回传成功,传输了 " + transferredBytes + " 字节数据。");
} catch (IOException e) {
  e.printStackTrace();
}

3.2.3.阻塞与非阻塞

ServerSocketChannel、SocketChannel支持两种阻塞模式:

  • 阻塞模式,遇到阻塞操作产生阻塞的时候会直接阻塞。
  • 非阻塞模式,遇到阻塞操作产生阻塞的时候会直接返回null。

默认都是阻塞模式,可以手动设置为非阻塞模式。

代码示例:

//准备buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//创建服务器
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
//设置为非阻塞模式
//serverSocketChannel.configureBlocking(false);
//绑定监听端口
serverSocketChannel.bind(new InetSocketAddress(8080));
//获取连接,这是一步阻塞操作,阻塞模式下,没读到连接会在这一步阻塞;非阻塞模式下不会阻塞,会直接返回一个null
SocketChannel socketChannel = serverSocketChannel.accept();
//设置为非阻塞模式
//socketChannel.configureBlocking(false);
//读数据,这是一步阻塞操作
if(socketChannel!=null) {
   //阻塞模式下,没读到连接会在这一步阻塞;非阻塞模式下不会阻塞,会直接返回一个null
   socketChannel.read(buffer);
}

4.selector

4.1.概述

selector,NIO实现非阻塞式IO的核心,它的功能很简单,就是用事件机制来监听channel,挑选出触发事件的channel。

我们知道,如果线程中有IO操作,IO没有完成,资源没有准备好之前,线程是会进入阻塞状态的。我们可以用单线程起一个selector去监听channel是否准备好数据,将准备好数据的channel挑选出来交给其它线程去处理,这样就不会因为IO资源没准备好导致线程阻塞。

我猜大家看到这里会有一个疑惑,什么时候会有上面描述的这种用线程去并发的处理IO?

网络通信的时候

如果用BIO的方式通信一进来就给一个线程去处理,那么就会有可能因为数据包还没收完,IO等待、阻塞,造成线程阻塞。而用NIO的话就可以用selector挑选出数据包收完的IO出来处理,不会有线程阻塞:

全网最清晰JAVA NIO,看一遍就会,JAVA SE,网络,java

selector的事件监听:

全网最清晰JAVA NIO,看一遍就会,JAVA SE,网络,java

通道的监听事件一共有如下类型:

既可以用常量表示,也可以用数字表示。

  • 读 SelectionKey.OP_READ 1
  • 写 SelectionKey.OP_WRITE 4
  • 连接 SelectionKey.OP_CONNECT 8
  • 接收 SelectionKey.OP_ACCEPT 16
  • 复合 若不止一个事件时,使用或操作符连接

4.2.代码示例

以下是用NIO进行非阻塞式网络通信的代码示例。

服务端:

//获取通道
ServerSocketChannel serverSocketChannel= ServerSocketChannel.open();
//切换为非阻塞模式
serverSocketChannel.configureBlocking(false);
//绑定连接的端口
serverSocketChannel.bind(new InetSocketAddress(9999));
//获取选择器
Selector selector=Selector.open();
//将通道注册到选择器上,并开始指定监听接收事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//轮询监听
while(selector.select()>0){
    Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
    while (iterator.hasNext()){
        SelectionKey selectionKey=iterator.next();
        //判当前socket的事件
        //1.接收事件(表示socket接收到了数据)
        if(selectionKey.isAcceptable()){
            SocketChannel socketChannel=serverSocketChannel.accept();
            //切换为非阻塞模式
            socketChannel.configureBlocking(false);
            //将通道以读就绪的事件重新注册到选择器
            socketChannel.register(selector,SelectionKey.OP_READ);
        }
        //2.读就绪事件
        if(selectionKey.isReadable()){
            SocketChannel socketChannel=(SocketChannel)selectionKey.channel();
            //读取数据
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            int length=0;
            while((length=socketChannel.read(byteBuffer))>0){
                byteBuffer.flip();
                System.out.println(new String(byteBuffer.array(),0,length));
                byteBuffer.clear();
            }
        }
        //事件处理完成,移除事件
        iterator.remove();
    }
}

客户端:文章来源地址https://www.toymoban.com/news/detail-601228.html

//获取通道
SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));
//切换成非阻塞模式
socketChannel.configureBlocking(false);
//分配缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//发送数据
Scanner scanner=new Scanner(System.in);
while(true){
    String msg=scanner.nextLine();
    byteBuffer.put(msg.getBytes());
    byteBuffer.flip();
    socketChannel.write(byteBuffer);
    byteBuffer.clear();
}

到了这里,关于全网最清晰JAVA NIO,看一遍就会的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java网络编程——NIO架构

    目录 1.什么是NIO 2.NIO结构 3.基于NIO下的聊天系统实现 4.Netty NIO:java non-blocking IO,同步非阻塞IO。 BIO是阻塞IO,即每一个事件都需要分配一个进程给他,如果客户端没有连接上,则一直阻塞等待。 而NIO,异步 I/O 是一种没有阻塞地读写数据的方法:该架构下我们可以注册对特定

    2023年04月26日
    浏览(43)
  • BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程

    上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的,比如 C10K 问题,其本质就是因其阻塞原因,导致如果想要承受更多的请求就必须有足够多的线程,但是足够多的线程会带来内存占用问题、CPU上下文切换带来的性能问题

    2024年02月14日
    浏览(35)
  • Day19-【Java SE进阶】网络编程

    一、网络编程 1.概述 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。 java.net,*包下提供了网络编程的解决方案! 基本的通信架构 基本的通信架构有2种形式:CS架构(Client客户端/Server服务端)、BS架构(Browser浏览器/Server服务端)。 网络通信的关键三要

    2024年04月14日
    浏览(26)
  • 《Java SE》网络编程基础知识归纳。

    目录 一、网络基本介绍 1、什么是网络通信? 2、网络 3、IP地址 4、域名 5、网络通信协议 6、Socket 二、TCP网络通信编程  1、应用实例1(字节流) 2、应用实例2(字节流) 3、应用实例3(字符流) 4、netstat 指令 三、UDP网络通信编程  1、基本介绍 2、基本流程 3、应用实例  

    2024年01月20日
    浏览(40)
  • Java 网络编程之NIO(selector)

                        本编文章意在循环渐进,可看最后一个就可以了                Selector selector = Selector.open();                首先channel必须是非阻塞的情况下                 channel.register(选择器,操作的类型,绑定的组件);返回的是选择键              1)Ch

    2023年04月11日
    浏览(32)
  • 快速入门java网络编程基础------Nio

    哔哩哔哩黑马程序员 netty实战视频 NIO(New I/O)是Java中提供的一种基于通道和缓冲区的I/O(Input/Output)模型。它是相对于传统的IO(InputStream和OutputStream)模型而言的新型I/O模型。NIO的主要特点包括: 1.通道与缓冲区: 2.NIO引入了通道(Channel)和缓冲区(Buffer)的概念。通道

    2024年01月20日
    浏览(37)
  • Java网络编程-深入理解BIO、NIO

    BIO BIO 为 Blocked-IO(阻塞 IO),在 JDK1.4 之前建立网络连接时,只能使用 BIO 使用 BIO 时,服务端会对客户端的每个请求都建立一个线程进行处理,客户端向服务端发送请求后,先咨询服务端是否有线程响应,如果没有就会等待或者被拒绝 BIO 基本使用代码: 服务端: 客户端:

    2024年02月04日
    浏览(30)
  • Java网络编程----通过实现简易聊天工具来聊聊NIO

    前文我们说过了BIO,今天我们聊聊NIO。 NIO 是什么?NIO官方解释它为 New lO ,由于其特性我们也称之为,Non-Blocking IO。这是jdk1.4之后新增的一套IO标准。 为什么要用NIO呢? 我们再简单回顾下BIO: 阻塞式IO,原理很简单,其实就是多个端点与服务端进行通信时,每个客户端有一个

    2024年02月05日
    浏览(49)
  • 快速入门Java NIO(Not I/O)的网络通信框架--Netty

    了解netty前需要对nio有一定认识,该笔记基础来自bilinbili黑马,在此基础上自己学习的笔记,添加了一些自己的理解 了解java 非阻塞io编程 1.1 Netty 是什么? Netty 是一个异步(基于多线程)的、基于事件驱动(多路复用的那写事件驱动)的网络应用框架,用于快速开发可维护、高性能的

    2024年01月17日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包