Java NIO原理图文分析及代码实现

Java NIO原理图文分析及代码实现

简介

Java NIO(Non-blocking I/O)是一种可替代Java标准I/O的I/O API。相比传统的I/O,Java NIO提供的I/O操作更快速、更灵活,并且支持更多的操作(如块传输和多路复用)。

基本组成部分

Java NIO的核心组件主要包含以下几个部分:

  • Channel(通道):基本的I/O操作(如写入和读取)在通道中完成。
  • Buffer(缓冲区):通道读写数据的存储区域,读取数据时首先将数据读入缓冲区,写入数据时首先将数据写入缓冲区。
  • Selector(选择器):用于多路复用I/O操作,同时能够检测某个通道上是否有事件发生。

Channel

Java NIO中的通道(Channel)类似于流(Stream)的概念,用于进行数据的读取和写入。不过,与流不同的是:

  • 通道是双向的,可以在一个通道上进行读取和写入操作。
  • 通道可以通过多个线程进行操作(前提是线程安全)。

Java NIO中的通道主要分为以下几类:

  • FileChannel:用于读取、写入和映射文件的通道。
  • DatagramChannel:通过UDP协议读取或写入网络数据。
  • SocketChannel:通过TCP协议进行网络连接的通道。
  • ServerSocketChannel:可以监听新进来的TCP连接,并且为每个新连接创建一个通道。

Buffer

缓冲区(Buffer)实际上就是一个内存块,通道读取或写入时通过缓冲区读写数据。在Java NIO中,缓冲区主要分为以下几种类型:

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

这些缓冲区类型的创建方法基本相同,可以通过以下方法进行创建:

Buffer buffer = ByteBuffer.allocate(1024);

缓冲区从内部来说,实际上就是一个数组。除了存储数据之外,缓冲区还提供了三个核心操作:

  • 写入数据到缓冲区:put()
  • 从缓冲区读取数据:get()
  • 读写模式切换:flip()

其中,读写模式切换是这样用的:

// 读模式
buffer.flip();

// 写模式
buffer.clear();

Selector

多路复用器(Selector)是一种可以同时处理多个通道的Java NIO组件。多路复用器基于底层操作系统提供的select()方法(或者epoll、KQueue等方法),可以检测某些事件是否发生。

多路复用器的运行主要流程如下:

  1. 创建多路复用器(Selector)。
  2. 将各个通道注册到多路复用器上(SocketChannel.register())。
  3. 轮询多路复用器中发生事件的通道(Selector.select())。
  4. 处理具体的事件(如连接、读取、写入等)。

示例说明

示例1:使用FileChannel读取文件

public static void readFile(String filePath) throws IOException {
    // 获取文件的FileChannel
    File file = new File(filePath);
    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
    FileChannel fileChannel = randomAccessFile.getChannel();

    // 创建buffer
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while (fileChannel.read(buffer) != -1) {
        // 切换到读模式
        buffer.flip();

        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }

        // 切换到写模式
        buffer.clear();
    }

    // 关闭FileChannel
    fileChannel.close();
}

以上代码中,我们首先通过RandomAccessFile获取到需要读取的文件,然后通过getChannel()方法获取到文件的FileChannel。接下来,我们创建一个ByteBuffer缓冲区,通过FileChannelread()方法从文件通道读取数据,并通过ByteBufferflip()方法将缓冲区切换到读模式。最后,我们通过ByteBufferget()方法从缓冲区读取数据,并在控制台上输出。当ByteBuffer中没有数据时,我们将缓冲区切换到写模式,并继续执行下一轮读操作,直到文件读取完毕。

示例2:使用ServerSocketChannel监听并处理连接

public static void startServer(int port) throws IOException {
    // 创建ServerSocketChannel
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    // 绑定端口
    serverSocketChannel.socket().bind(new InetSocketAddress(port));

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

    // 创建Selector
    Selector selector = Selector.open();

    // 将ServerSocketChannel注册到Selector上,并监听连接事件
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    while (true) {
        // 阻塞等待事件
        selector.select();

        // 获取事件和事件通道
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        Iterator<SelectionKey> it = selectionKeys.iterator();

        // 处理事件
        while (it.hasNext()) {
            SelectionKey selectionKey = it.next();
            it.remove();

            if (selectionKey.isAcceptable()) {
                // 处理连接事件
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            } else if (selectionKey.isReadable()) {
                // 处理读取事件
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                socketChannel.read(buffer);
                buffer.flip();
                String message = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
                System.out.println("读取信息:" + message);
                socketChannel.write(ByteBuffer.wrap(("收到信息:" + message).getBytes()));
                socketChannel.close();
            }
        }
    }
}

以上代码中,我们首先创建一个ServerSocketChannel,并将它的端口绑定到我们要监听的端口上。然后,我们将ServerSocketChannel注册到Selector中并监听连接事件(SelectionKey.OP_ACCEPT)。接下来,我们进入一个无限循环,调用Selectorselect()方法模拟轮询事件。当select()返回时,我们调用SelectorselectedKeys()方法获取到所有发生的事件。然后,我们遍历选择键(Selection Key)集合,并处理每一个事件。当我们监听到一个连接事件时,我们通过ServerSocketChannelaccept()方法接受连接,并将其注册到Selector中监听读事件。当我们监听到读事件时,我们可以通过SocketChannelread()方法读取客户端发送的数据,并在控制台上输出。最后,我们通过SocketChannelwrite()方法向客户端发送数据,然后关闭连接。

总结

Java NIO提供了一种新的I/O操作方式,相较于传统I/O具有更高的处理效率和更灵活的使用方式,这在某些高并发场景下会提供很大的帮助作用。具体的操作与实现方式还需要在实践中去逐步掌握。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java NIO原理图文分析及代码实现 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • SpringData Repository接口用法解析

    SpringData Repository接口用法解析 Spring Data是Spring提供的一组库和工具,用于简化数据库访问和操作。其中,Spring Data Repository是一个简化数据访问层实现的框架,提供了一种基于接口的形式,可以自动生成实现类的方法,大大减少了实现数据访问层和数据库操作的代码工作量。本文将介绍SpringData Rep…

    Java 2023年5月20日
    00
  • Java8 Lambda表达式详解及实例

    Java8 Lambda表达式详解及实例 什么是Lambda表达式 Lambda表达式是Java8中引入的一个新特性,是一种轻量级的匿名函数,用来替代过往繁琐的匿名内部类编写方式。Lambda表达式可以被赋值到一个变量中,或者传递到一个方法中作为参数,像对象一样使用。Lambda表达式的语法简洁、优雅,让Java8代码的可读性和可维护性更加强大。 Lambd…

    Java 2023年5月26日
    00
  • Java日常练习题,每天进步一点点(35)

    下面是完整的攻略: 概述 Java日常练习题是一系列Java练手题,旨在帮助Java初学者熟悉Java语言,加深对Java知识的理解。本篇题目为第35题,难度为中等。 题目描述 写一个Java程序,输入一个字符串,输出其中不重复的字符。 解题思路 我们可以使用HashMap来存储每个字符出现的次数,然后遍历HashMap,输出出现次数为1的字符即可。 以下是…

    Java 2023年5月19日
    00
  • Listener监听器,实现一个显示在线用户人数

    Listener监听器,实现一个显示在线用户人数 每博一文案 关于后半身,脾气越温,福报越深。 师傅说:惜命最好的方式不是养生,而是管好自己的情绪。 坏毛病都是惯出来的,但好脾气都是磨出来的,与人生气,伤的是和气,与自己生气,伤的是身体。 佛说:人有五毒心,贪嗔痴慢疑,其中一时的嗔念起,百万叶障深,火烧功德林,脾气来了,福气就走了。 破得了偏执,才修得了善行…

    Java 2023年5月9日
    00
  • Java设置String字符串编码方法详解

    Java设置String字符串编码方法详解 在Java中,字符串编码是非常重要的一个概念,它涉及到字符串在不同系统之间的传输和存储,如果不正确地处理编码会导致乱码或者其他不可预计的问题。本文将详细介绍Java中设置字符串编码的方法,帮助读者更好地掌握这一知识。 字符串编码介绍 在计算机中,一切都是二进制的,因此字符串也需要通过编码方式将其转换为二进制,才能在…

    Java 2023年5月20日
    00
  • Java Spring Bean的生命周期管理详解

    Java Spring Bean的生命周期管理详解 简介 在使用Spring框架时,Bean(实例)的生命周期管理是非常重要的,它涉及到Bean的创建、初始化、依赖注入、方法调用、销毁等过程。本文将详细介绍Java Spring中Bean的生命周期管理机制,帮助读者更好地理解和使用Spring框架。 生命周期阶段 在Spring框架中,Bean的生命周期可以…

    Java 2023年5月31日
    00
  • gaussdb 200安装 data studio jdbc idea链接保姆级安装步骤

    下面是详细的 “gaussdb 200安装 data studio jdbc idea链接保姆级安装步骤”攻略: 准备工作 首先需要从官网下载并安装GaussDB 200数据库软件; 安装Java开发环境,假设你已经安装了Java环境,那么请确认你的Java版本号,因为后续需要使用JDBC链接数据库,而不同版本的jdbc驱动有差异; 安装Intellij I…

    Java 2023年6月16日
    00
  • Springboot整合企业微信机器人助手推送消息的实现

    什么是企业微信机器人助手? 企业微信机器人助手是企业微信推出的一款机器人应用,旨在方便企业在企业微信中进行消息推送、管理和协作等操作。企业微信机器人助手可以通过API接口,实现与企业自有的应用进行对接。 Springboot整合企业微信机器人助手的实现过程 下面我们来讲一下如何在Springboot中整合企业微信机器人助手,实现推送消息的功能。 2.1 准备…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部