当前位置: 首页 > news >正文

Java NIO 核心知识总结

在学习 NIO 之前,需要先了解一下计算机 I/O 模型的基础理论知识。还不了解的话,可以参考我写的这篇文章:Java IO 模型详解。

一、NIO 简介

在传统的 Java I/O 模型(BIO)中,I/O 操作是以阻塞的方式进行的。也就是说,当一个线程执行一个 I/O 操作时,它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈,因为需要为每个连接创建一个线程,而线程的创建和切换都是有开销的。

为了解决这个问题,在 Java1.4 版本引入了一种新的 I/O 模型 — NIO (New IO,也称为 Non-blocking IO) 。NIO 弥补了同步阻塞 I/O 的不足,它在标准 Java 代码中提供了非阻塞、面向缓冲、基于通道的 I/O,可以使用少量的线程来处理多个连接,大大提高了 I/O 效率和并发。

下图是 BIO、NIO 和 AIO 处理客户端请求的简单对比图(关于 AIO 的介绍,可以看我写的这篇文章:Java IO 模型详解,不是重点,了解即可)。

⚠️需要注意:使用 NIO 并不一定意味着高性能,它的性能优势主要体现在高并发和高延迟的网络环境下。当连接数较少、并发程度较低或者网络传输速度较快时,NIO 的性能并不一定优于传统的 BIO 

二、工作原理

  1. 非阻塞模式

    • Java NIO的非阻塞模式使得一个线程可以从某个通道发送或读取数据,但它仅能得到当前可用的数据。如果没有数据可用,线程不会被阻塞,而是可以继续做其他事情。

    • 这种非阻塞模式提高了线程的利用率和应用的性能。

  2. 缓冲区操作

    • 在NIO中,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

    • 缓冲区提供了对数据读写的灵活性和高效性。通过缓冲区的position、limit和capacity属性,可以精确地控制数据的读写操作。

  3. 选择器监听

    • 选择器通过监听多个通道的事件来管理多个输入和输出通道。

    • 当某个通道的事件发生时(如连接请求、数据到达等),选择器会返回并告知哪些通道的事件已经就绪。

    • 线程可以根据选择器返回的信息来处理相应的事件。

三、NIO 核心组件

NIO 主要包括以下三个核心组件:

  • Buffer(缓冲区)

    • NIO 读写数据都是通过缓冲区进行操作的。读操作的时候将 Channel 中的数据填充到 Buffer 中,而写操作时将 Buffer 中的数据写入到 Channel 中。

    • 缓冲区是一个容器对象,它实质上是一个数组。在NIO中,所有数据都是用缓冲区处理的。缓冲区提供了对数据读写的灵活性和高效性。

    • 常用的缓冲区类型包括:ByteBuffer(字节缓冲区)、CharBuffer(字符缓冲区)、IntBuffer(整数缓冲区)、LongBuffer(长整型缓冲区)、DoubleBuffer(双精度浮点缓冲区)和FloatBuffer(单精度浮点缓冲区)。

    • 缓冲区有三个关键属性:capacity(容量)、position(位置)和limit(限制)。capacity表示缓冲区的总大小,position表示当前读写操作的位置,limit表示当前可以操作的最大位置。

  • Channel(通道)

    • Channel 是一个双向的、可读可写的数据传输通道,NIO 通过 Channel 来实现数据的输入输出。通道是一个抽象的概念,它可以代表文件、套接字或者其他数据源之间的连接。

    • 通道是NIO中用于读写数据的通道,它类似于传统的流,但提供了更高的性能和更多的功能。

    • 通道可以是双向的,这意味着它既可以从通道读取数据,也可以向通道写入数据。

    • 常用的通道类型包括:FileChannel(文件通道)、SocketChannel(套接字通道)、ServerSocketChannel(服务器套接字通道)和DatagramChannel(数据报通道)。

  • Selector(选择器)

    • 允许一个线程处理多个 Channel,基于事件驱动的 I/O 多路复用模型。所有的 Channel 都可以注册到 Selector 上,由 Selector 来分配线程来处理事件。

    • 选择器是NIO中的一个重要组件,它允许一个线程监视多个通道的事件(如连接请求、数据到达等)。

    • 通过选择器,一个线程可以管理多个输入和输出通道,从而提高了网络应用的性能和响应速度。

    • 使用选择器时,需要先将通道注册到选择器上,并指定感兴趣的事件类型。然后,通过调用选择器的select()方法来等待事件的发生。一旦有事件发生,选择器就会返回并告知哪些通道的事件已经就绪。

三者的关系如下图所示(暂时不理解没关系,后文会详细介绍):

下面详细介绍一下这三个组件。

1、Buffer(缓冲区)

在传统的 BIO 中,数据的读写是面向流的, 分为字节流和字符流。

在 Java 1.4 的 NIO 库中,所有数据都是用缓冲区处理的,这是新库和之前的 BIO 的一个重要区别,有点类似于 BIO 中的缓冲流。NIO 在读取数据时,它是直接读到缓冲区中的。在写入数据时,写入到缓冲区中。 使用 NIO 在读写数据时,都是通过缓冲区进行操作。

Buffer 的子类如下图所示。其中,最常用的是 ByteBuffer,它可以用来存储和操作字节数据。

你可以将 Buffer 理解为一个数组,IntBufferFloatBufferCharBuffer 等分别对应 int[]float[]char[] 等。

为了更清晰地认识缓冲区,我们来简单看看Buffer 类中定义的四个成员变量:

public abstract class Buffer {// Invariants: mark <= position <= limit <= capacityprivate int mark = -1;private int position = 0;private int limit;private int capacity;
}

这四个成员变量的具体含义如下:

  1. 容量(capacity):Buffer可以存储的最大数据量,Buffer创建时设置且不可改变;

  2. 界限(limit):Buffer 中可以读/写数据的边界。写模式下,limit 代表最多能写入的数据,一般等于 capacity(可以通过limit(int newLimit)方法设置);读模式下,limit 等于 Buffer 中实际写入的数据大小。

  3. 位置(position):下一个可以被读写的数据的位置(索引)。从写操作模式到读操作模式切换的时候(flip),position 都会归零,这样就可以从头开始读写了。

  4. 标记(mark):Buffer允许将位置直接定位到该标记处,这是一个可选属性;

并且,上述变量满足如下的关系:0 <= mark <= position <= limit <= capacity

另外,Buffer 有读模式和写模式这两种模式,分别用于从 Buffer 中读取数据或者向 Buffer 中写入数据。Buffer 被创建之后默认是写模式,调用 flip() 可以切换到读模式。如果要再次切换回写模式,可以调用 clear() 或者 compact() 方法。

Buffer 对象不能通过 new 调用构造方法创建对象 ,只能通过静态方法实例化 Buffer

这里以 ByteBuffer为例进行介绍:

// 分配堆内存
public static ByteBuffer allocate(int capacity);
// 分配直接内存
public static ByteBuffer allocateDirect(int capacity);

Buffer 最核心的两个方法:

  1. get : 读取缓冲区的数据

  2. put向缓冲区写入数据

除上述两个方法之外,其他的重要方法:

  • flip将缓冲区从写模式切换到读模式,它会将 limit 的值设置为当前 position 的值,将 position 的值设置为 0。

  • clear:  清空缓冲区,将缓冲区从读模式切换到写模式,并将 position 的值设置为 0,将 limit 的值设置为 capacity 的值。

Buffer 中数据变化的过程:

import java.nio.*;public class CharBufferDemo {public static void main(String[] args) {// 分配一个容量为8的CharBufferCharBuffer buffer = CharBuffer.allocate(8);System.out.println("初始状态:");printState(buffer);// 向buffer写入3个字符buffer.put('a').put('b').put('c');System.out.println("写入3个字符后的状态:");printState(buffer);// 调用flip()方法,准备读取buffer中的数据,将 position 置 0,limit 的置 3buffer.flip();System.out.println("调用flip()方法后的状态:");printState(buffer);// 读取字符while (buffer.hasRemaining()) {System.out.print(buffer.get());}// 调用clear()方法,清空缓冲区,将 position 的值置为 0,将 limit 的值置为 capacity 的值buffer.clear();System.out.println("调用clear()方法后的状态:");printState(buffer);}// 打印buffer的capacity、limit、position、mark的位置private static void printState(CharBuffer buffer) {System.out.print("capacity: " + buffer.capacity());System.out.print(", limit: " + buffer.limit());System.out.print(", position: " + buffer.position());System.out.print(", mark 开始读取的字符: " + buffer.mark());System.out.println("\n");}
}

输出:

初始状态:
capacity: 8, limit: 8, position: 0

写入3个字符后的状态:
capacity: 8, limit: 8, position: 3

准备读取buffer中的数据!

调用flip()方法后的状态:
capacity: 8, limit: 3, position: 0

读取到的数据:abc

调用clear()方法后的状态:
capacity: 8, limit: 8, position: 0

为了帮助理解,我绘制了一张图片展示 capacitylimitposition每一阶段的变化。

2、Channel(通道)

Channel 是一个通道,它建立了与数据源(如文件、网络套接字等)之间的连接。我们可以利用它来读取和写入数据,就像打开了一条自来水管,让数据在 Channel 中自由流动。

BIO 中的流是单向的,分为各种 InputStream(输入流)和 OutputStream(输出流),数据只是在一个方向上传输。通道与流的不同之处在于通道是双向的,它可以用于读、写或者同时用于读写。

Channel 与前面介绍的 Buffer 打交道,读操作的时候将 Channel 中的数据填充到 Buffer 中,而写操作时将 Buffer 中的数据写入到 Channel 中。

另外,因为 Channel 是全双工的,所以它可以比流更好地映射底层操作系统的 API。特别是在 UNIX 网络编程模型中,底层操作系统的通道都是全双工的,同时支持读写操作。

Channel 的子类如下图所示。

其中,最常用的是以下几种类型的通道:

  • FileChannel文件访问通道;

  • SocketChannelServerSocketChannelTCP 通信通道;

  • DatagramChannelUDP 通信通道;

Channel 最核心的两个方法:

  1. read读取数据并写入到 Buffer 中。

  2. write将 Buffer 中的数据写入到 Channel 中。

这里我们以 FileChannel 为例演示一下是读取文件数据的。

RandomAccessFile reader = new RandomAccessFile("/Users/guide/Documents/test_read.in", "r"))
FileChannel channel = reader.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);

3、Selector(选择器)

Selector(选择器) 是 NIO 中的一个关键组件,它允许一个线程处理多个 Channel。Selector 是基于事件驱动的 I/O 多路复用模型,主要运作原理是:通过 Selector 注册通道的事件,Selector 会不断地轮询注册在其上的 Channel。当事件发生时,比如:某个 Channel 上面有新的 TCP 连接接入、读和写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来。Selector 会将相关的 Channel 加入到就绪集合中。通过 SelectionKey 可以获取就绪 Channel 的集合,然后对这些就绪的 Channel 进行相应的 I/O 操作。

一个多路复用器 Selector 可以同时轮询多个 Channel,由于 JDK 使用了 epoll() 代替传统的 select 实现,所以它并没有最大连接句柄 1024/2048 的限制。这也就意味着只需要一个线程负责 Selector 的轮询,就可以接入成千上万的客户端。

Selector 可以监听以下四种事件类型:

  1. SelectionKey.OP_ACCEPT表示通道接受连接的事件,这通常用于 ServerSocketChannel

  2. SelectionKey.OP_CONNECT表示通道完成连接的事件,这通常用于 SocketChannel

  3. SelectionKey.OP_READ表示通道准备好进行读取的事件,即有数据可读。

  4. SelectionKey.OP_WRITE表示通道准备好进行写入的事件,即可以写入数据。

Selector是抽象类,可以通过调用此类的 open() 静态方法来创建 Selector 实例。Selector 可以同时监控多个 SelectableChannelIO 状况,是非阻塞 IO 的核心。

一个 Selector 实例有三个 SelectionKey 集合:

  1. 所有的 SelectionKey 集合:代表了注册在该 Selector 上的 Channel,这个集合可以通过 keys() 方法返回。

  2. 被选择的 SelectionKey 集合:代表了所有可通过 select() 方法获取的、需要进行 IO 处理的 Channel,这个集合可以通过 selectedKeys() 返回。

  3. 被取消的 SelectionKey 集合:代表了所有被取消注册关系的 Channel,在下一次执行 select() 方法时,这些 Channel 对应的 SelectionKey 会被彻底删除,程序通常无须直接访问该集合,也没有暴露访问的方法。

简单演示一下如何遍历被选择的 SelectionKey 集合并进行处理:

Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key != null) {if (key.isAcceptable()) {// ServerSocketChannel 接收了一个新连接} else if (key.isConnectable()) {// 表示一个新连接建立} else if (key.isReadable()) {// Channel 有准备好的数据,可以读取} else if (key.isWritable()) {// Channel 有空闲的 Buffer,可以写入数据}}keyIterator.remove();
}

Selector 还提供了一系列和 select() 相关的方法:

  • int select()监控所有注册的 Channel,当它们中间有需要处理的 IO 操作时,该方法返回,并将对应的 SelectionKey 加入被选择的 SelectionKey 集合中,该方法返回这些 Channel 的数量。

  • int select(long timeout)可以设置超时时长的 select() 操作。

  • int selectNow()执行一个立即返回的 select() 操作,相对于无参数的 select() 方法而言,该方法不会阻塞线程。

  • Selector wakeup()使一个还未返回的 select() 方法立刻返回。

使用 Selector 实现网络读写的简单示例:

import java.io.IOException;
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.util.Iterator;
import java.util.Set;public class NioSelectorExample {public static void main(String[] args) {try {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.socket().bind(new InetSocketAddress(8080));Selector selector = Selector.open();// 将 ServerSocketChannel 注册到 Selector 并监听 OP_ACCEPT 事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {int readyChannels = selector.select();if (readyChannels == 0) {continue;}Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {// 处理连接事件ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();client.configureBlocking(false);// 将客户端通道注册到 Selector 并监听 OP_READ 事件client.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {// 处理读事件SocketChannel client = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = client.read(buffer);if (bytesRead > 0) {buffer.flip();System.out.println("收到数据:" +new String(buffer.array(), 0, bytesRead));// 将客户端通道注册到 Selector 并监听 OP_WRITE 事件client.register(selector, SelectionKey.OP_WRITE);} else if (bytesRead < 0) {// 客户端断开连接client.close();}} else if (key.isWritable()) {// 处理写事件SocketChannel client = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.wrap("Hello, Client!".getBytes());client.write(buffer);// 将客户端通道注册到 Selector 并监听 OP_READ 事件client.register(selector, SelectionKey.OP_READ);}keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}
}

在示例中,我们创建了一个简单的服务器,监听 8080 端口,使用 Selector 处理连接、读取和写入事件。当接收到客户端的数据时,服务器将读取数据并将其打印到控制台,然后向客户端回复 "Hello, Client!"。

四、NIO 零拷贝

零拷贝是提升 IO 操作性能的一个常用手段,像 ActiveMQ、Kafka 、RocketMQ、QMQ、Netty 等顶级开源项目都用到了零拷贝。

零拷贝是指计算机执行 IO 操作时,CPU 不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及 CPU 的拷贝时间。也就是说,零拷贝主要解决操作系统在处理 I/O 操作时频繁复制数据的问题。零拷贝的常见实现技术有: mmap+writesendfilesendfile + DMA gather copy

下图展示了各种零拷贝技术的对比图:

CPU 拷贝DMA 拷贝系统调用上下文切换
传统方法22read+write4
mmap+write12mmap+write4
sendfile12sendfile2
sendfile + DMA gather copy02sendfile2

可以看出,无论是传统的 I/O 方式,还是引入了零拷贝之后,2 次 DMA(Direct Memory Access) 拷贝是都少不了的。因为两次 DMA 都是依赖硬件完成的。零拷贝主要是减少了 CPU 拷贝及上下文的切换。

Java 对零拷贝的支持:

  • MappedByteBuffer 是 NIO 基于内存映射(mmap)这种零拷⻉⽅式的提供的⼀种实现,底层实际是调用了 Linux 内核的 mmap 系统调用。它可以将一个文件或者文件的一部分映射到内存中,形成一个虚拟内存文件,这样就可以直接操作内存中的数据,而不需要通过系统调用来读写文件。
  • FileChanneltransferTo()/transferFrom()是 NIO 基于发送文件(sendfile)这种零拷贝方式的提供的一种实现,底层实际是调用了 Linux 内核的 sendfile系统调用。它可以直接将文件数据从磁盘发送到网络,而不需要经过用户空间的缓冲区。关于FileChannel的用法可以看看这篇文章:Java NIO 文件通道 FileChannel 用法。

代码示例:

private void loadFileIntoMemory(File xmlFile) throws IOException {FileInputStream fis = new FileInputStream(xmlFile);// 创建 FileChannel 对象FileChannel fc = fis.getChannel();// FileChannel.map() 将文件映射到直接内存并返回 MappedByteBuffer 对象MappedByteBuffer mmb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());xmlFileBuffer = new byte[(int)fc.size()];mmb.get(xmlFileBuffer);fis.close();
}

总结

这篇文章我们主要介绍了 NIO 的核心知识点,包括 NIO 的核心组件和零拷贝。

如果我们需要使用 NIO 构建网络程序的话,不建议直接使用原生 NIO,编程复杂且功能性太弱,推荐使用一些成熟的基于 NIO 的网络编程框架比如 Netty。Netty 在 NIO 的基础上进行了一些优化和扩展比如支持多种协议、支持 SSL/TLS 等等

相关文章:

Java NIO 核心知识总结

在学习 NIO 之前&#xff0c;需要先了解一下计算机 I/O 模型的基础理论知识。还不了解的话&#xff0c;可以参考我写的这篇文章&#xff1a;Java IO 模型详解。 一、NIO 简介 在传统的 Java I/O 模型&#xff08;BIO&#xff09;中&#xff0c;I/O 操作是以阻塞的方式进行的。…...

疑难Tips:NextCloud域名访问登录时卡住,显示违反内容安全策略

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ] 1使用域名访问Nextcloud用户登录时卡住,显示违反内容安全策略 我使用官方Docker镜像来部署NextCloud 28.0.5,并通过Openresty反向代理Nextcloud,但是在安装后无法稳定工作,每次登录后,页面会卡死在登录界面,无法…...

C 语言学习-06【指针】

1、目标单元与简介存取 直接访问和间接访问 #include <stdio.h>int main(void) {int a 3, *p;p &a;printf("a %d, *p %d\n", a, *p);*p 10;printf("a %d, *p %d\n", a, *p);printf("Enter a: ");scanf("%d", &a)…...

如何快速将Excel数据导入到SQL Server数据库

工作中&#xff0c;我们经常需要将Excel数据导入到数据库&#xff0c;但是对于数据库小白来说&#xff0c;这可能并非易事&#xff1b;对于数据库专家来说&#xff0c;这又可能非常繁琐。 这篇文章将介绍如何帮助您快速的将Excel数据导入到sql server数据库。 准备工作 这里&…...

【人工智能】Python在机器学习与人工智能中的应用

Python因其简洁易用、丰富的库支持以及强大的社区&#xff0c;被广泛应用于机器学习与人工智能&#xff08;AI&#xff09;领域。本教程通过实用的代码示例和讲解&#xff0c;带你从零开始掌握Python在机器学习与人工智能中的基本用法。 1. 机器学习与AI的Python生态系统 Pyth…...

使用八爪鱼爬虫抓取汽车网站数据,分析舆情数据

我是做汽车行业的&#xff0c;可以用八爪鱼爬虫抓取汽车之家和微博上的汽车文章内容&#xff0c;分析各种电动汽车口碑数据。 之前&#xff0c;我写过很多Python网络爬虫的案例&#xff0c;使用requests、selenium等技术采集数据&#xff0c;这次尝试去采集小米SU7在微博、汽车…...

什么是事务?事务有哪些特性?

在数据库管理中&#xff0c;事务是一个核心概念&#xff0c;它确保了数据操作的完整性和一致性。本文将探讨事务的定义及其四大特性。 一、事务的定义 事务是数据库操作的最小工作单元&#xff0c;是作为单个逻辑工作单元执行的一系列操作。这些操作作为一个整体一起向系统提…...

玩转合宙Luat教程 基础篇④——程序基础(库、线程、定时器和订阅/发布)

文章目录 一、前言二、库三、线程四、定时器五、订阅/发布5.1 回调函数5.2 堵塞等待一、前言 教程目录大纲请查阅:玩转合宙Luat教程——导读 写一写Lua程序基础的东西。 包括如何调用库,如何创建线程、如何创建定时器,如何使用订阅/发布事件。 二、库 程序从main.lua开始通…...

24.<Spring博客系统①(数据库+公共代码+持久层+显示博客列表+博客详情)>

项目整体预览 登录页面 主页 查看全文 编辑 写博客 PS&#xff1a;Service.impl&#xff08;现在流行写法&#xff09; 推荐写法。后续完成项目。会尝试这样写。 接口可以有多个实现。每个实现都可以不同。 这也算一种设计模式。叫做&#xff08;策略模式&#xff09;。 我们…...

webp 网页如何录屏?

工作中正好研究到了一点&#xff1a;记录下这里&#xff1a; 先看下效果&#xff1a; 具体实现代码&#xff1a; &#xfeff; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

丹摩征文活动|实现Llama3.1大模型的本地部署

文章目录 1.前言2.丹摩的配置3.Llama3.1的本地配置4. 最终界面 丹摩 1.前言 Llama3.1是Meta 公司发布的最新开源大型语言模型&#xff0c;相较于之前的版本&#xff0c;它在规模和功能上实现了显著提升&#xff0c;尤其是最大的 4050亿参数版本&#xff0c;成为开源社区中非常…...

Spring Boot 2 和 Spring Boot 3 中使用 Spring Security 的区别

文章目录 Spring Boot 2 和 Spring Boot 3 中使用 Spring Security 的区别1. Jakarta EE 迁移2. Spring Security 配置方式的变化3. PasswordEncoder 加密方式的变化4. permitAll() 和 authenticated() 的变化5. 更强的默认安全设置6. Java 17 支持与语法提升7. PreAuthorize、…...

【数据结构与算法】 LeetCode:回溯

文章目录 回溯算法组合组合总和&#xff08;Hot 100&#xff09;组合总和 II电话号码的字母组合&#xff08;Hot 100&#xff09;括号生成&#xff08;Hot 100&#xff09;分割回文串&#xff08;Hot 100&#xff09;复原IP地址子集&#xff08;Hot 100&#xff09;全排列&…...

SpringBoot线程池的使用

SpringBoot线程池的使用 在现代Web应用开发中&#xff0c;特别是在使用Spring Boot框架时&#xff0c;合理使用线程池可以显著提高应用的性能和响应速度。线程池不仅能够减少线程创建和销毁的开销&#xff0c;还能有效地控制并发任务的数量&#xff0c;避免因线程过多而导致的…...

Neural Magic 发布 LLM Compressor:提升大模型推理效率的新工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

HttpServletRequest req和前端的关系,req.getParameter详细解释,req.getParameter和前端的关系

HttpServletRequest 对象在后端和前端之间起到了桥梁的作用&#xff0c;它包含了来自客户端的所有请求信息。通过 HttpServletRequest 对象&#xff0c;后端可以获取前端发送的请求参数、请求头、请求方法等信息&#xff0c;并根据这些信息进行相应的处理。以下是对 HttpServle…...

React-useEffect的使用

useEffect react提供的一个常用hook&#xff0c;用于在函数组件中执行副作用操作&#xff0c;比如数据获取、订阅或手动更改DOM。 基本用法&#xff1a; 接受2个参数&#xff1a; 一个包含命令式代码的函数&#xff08;副作用函数&#xff09;。一个依赖项数组&#xff0c;用…...

MySQL数据库与Informix:能否创建同名表?

MySQL数据库与Informix:能否创建同名表? 一、MySQL数据库中的同名表创建1. 使用CREATE TABLE ... SELECT语句2. 使用CREATE TABLE LIKE语句3. 复制表结构并选择性复制数据4. 使用同义词(Synonym)二、Informix数据库中的同名表创建1. 使用不同所有者2. 使用不同模式3. 复制表…...

爬虫实战:采集知乎XXX话题数据

目录 反爬虫的本意和其带来的挑战目标实战开发准备代码开发发现问题1. 发现问题[01]2. 发现问题[02] 解决问题1. 解决问题[01]2. 解决问题[02] 最终结果 结语 反爬虫的本意和其带来的挑战 在这个数字化时代社交媒体已经成为人们表达观点的重要渠道&#xff0c;对企业来说&…...

大数据新视界 -- Hive 数据桶原理:均匀分布数据的智慧(上)(9/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

理想汽车5月交付40856辆,同比增长16.7%

6月1日&#xff0c;理想汽车官方宣布&#xff0c;5月交付新车40856辆&#xff0c;同比增长16.7%。截至2025年5月31日&#xff0c;理想汽车历史累计交付量为1301531辆。 官方表示&#xff0c;理想L系列智能焕新版在5月正式发布&#xff0c;全系产品力有显著的提升&#xff0c;每…...

Vue 实例的数据对象详解

Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...

RFID推动新能源汽车零部件生产系统管理应用案例

RFID推动新能源汽车零部件生产系统管理应用案例 一、项目背景 新能源汽车零部件场景 在新能源汽车零部件生产领域&#xff0c;电子冷却水泵等关键部件的装配溯源需求日益增长。传统 RFID 溯源方案采用 “网关 RFID 读写头” 模式&#xff0c;存在单点位单独头溯源、网关布线…...