从BIO到NIO到AIO: Java全面IO模型解析

1. Java IO模型概述

Java IO(输入/输出)是Java编程语言中用于数据输入和输出的一组功能强大的API。这些API为文件IO、网络IO以及系统资源IO提供了丰富的类和接口。由于IO操作直接与操作系统交互,因此理解Java IO模型与操作系统模型如何联系是至关重要的。

1.1 简介Java IO

在Java中,IO操作是通过使用java.io包中的类和接口执行的。java.io包提供了非常丰富的流(Stream)类别进行数据读写,这些流类别主要分为两大部分:字节流(例如InputStream和OutputStream)用于处理RAW数据如二进制文件,字符流(例如Reader和Writer)用于处理字符和字符串,更适用于文本数据。

1.2 Java IO与操作系统模型的联系

Java的IO模型在底层是建立在操作系统的文件描述符之上的。无论是Windows还是类Unix系统,操作系统都提供了对文件进行操作的底层调用。Java IO通过JVM(Java虚拟机)调用本地方法,转而利用操作系统提供的系统调用来实现IO操作。

1.3 Java IO类库结构

Java IO类库提供了各种各样的流类,基本上都遵循了装饰器设计模式。在Java IO中,最基本的抽象类是InputStream和OutputStream,而对于字符操作则是Reader和Writer。它们为处理不同类型的数据提供基础。在这个基础上,Java提供了装饰器类,比如BufferedInputStream和BufferedReader,它们对基本的流进行了封装,提供了更高效的方法进行IO操作。

import java.io.*;public class IODemo {public static void main(String[] args) throws IOException {// 使用FileInputStream读取文件内容InputStream is = new FileInputStream("example.txt");int data = is.read();while(data != -1){System.out.print((char) data);data = is.read();}is.close();// 使用BufferedReader读取文件内容,更高效BufferedReader br = new BufferedReader(new FileReader("example.txt"));String line;while((line = br.readLine()) != null){System.out.println(line);}br.close();}
}

2. 阻塞IO模型(BIO)

在Java中,传统的IO模型被称为阻塞IO(BIO)。BIO是基于流模型实现的,它在执行IO操作时会导致线程暂停执行直到有数据可读,或者数据完全写入。这意味着如果没有数据可读,或者写入操作阻塞,线程会一直等待在那里。这种模式简单易懂,但不适合处理并发操作,因此在多用户或高负载的环境中效率较低。

2.1 BIO概念及其工作原理

BIO工作在阻塞模式下,当一个线程调用read()或write()时,它会阻塞住直到某个特定的条件满足:对于读操作,它会等待直到输入数据可用;对于写操作,则会等待直到可以将数据写入。这意味着在IO操作完成之前,该线程不能做任何其他的事情。

2.2 BIO操作模式与案例解析

以服务器端接收客户端连接的例子来分析BIO模式。在一个传统的客户端-服务器模型中,服务器为每个连接创建一个新的线程来处理请求。这种模式在连接数不多且负载较轻时运作良好。然而,在高并发场景下,每个连接都需要一个线程,这样会消耗大量系统资源,导致线程上下文切换频繁,大大降低了系统的可伸缩性和效率。
下面是一个服务器端接收客户端连接的简单Java示例,使用了BIO:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class BIOServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8080);System.out.println("等待连接...");while (true) {// 接收客户端连接,阻塞直到有新的连接建立Socket clientSocket = serverSocket.accept(); System.out.println("客户端连接成功");// 创建新线程处理连接new Thread(() -> {try {handleClient(clientSocket);} catch (IOException e) {e.printStackTrace();}}).start();}}private static void handleClient(Socket clientSocket) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);String request;while ((request = reader.readLine()) != null) {if ("quit".equals(request)) {break;}writer.println("Echo: " + request);System.out.println("处理数据: " + request);}}
}

这段代码展示了如何使用BIO模式创建一个简易的回显服务器。服务器端使用ServerSocket等待并接受客户端的连接请求,每当一个新的连接建立时,通过创建一个新的线程来处理该连接的读写请求。

3. JAVA IO包详解

JAVA IO包java.io提供了一套丰富的类用于数据流的输入和输出。无论是文件操作还是网络数据传输,这个包提供的类和接口都能够满足日常开发的需求。

3.1 File类的使用与文件操作

File类是java.io包中最基本的类之一,它的实例代表了磁盘上的文件路径。File类不仅可以用于表示文件和文件夹,还可以用于获取标准的文件属性,检查文件权限,操作路径等。

import java.io.File;
import java.io.IOException;public class FileOperations {public static void main(String[] args) throws IOException {// 创建File对象表示路径File file = new File("example.txt");// 基本文件操作if (!file.exists()) {// 如果文件不存在,则创建新文件boolean isCreated = file.createNewFile();System.out.println("文件创建:" + (isCreated ? "成功" : "失败或已存在"));}// 读取文件信息System.out.println("文件名:" + file.getName());System.out.println("文件路径:" + file.getPath());System.out.println("文件大小:" + file.length() + " 字节");// 删除文件// boolean isDeleted = file.delete();// System.out.println("文件删除:" + (isDeleted ? "成功" : "失败或文件不存在"));}
}

3.2 InputStream与OutputStream的原理及使用

InputStream和OutputStream是java.io包中用于读写二进制数据的基类。所有通过读写字节数据的IO类都是这两个类的子类。InputStream抽象了数据输入流的概念,OutputStream抽象了数据输出流的概念。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class StreamOperations {public static void main(String[] args) throws IOException {FileInputStream in = null;FileOutputStream out = null;try {in = new FileInputStream("input.txt");out = new FileOutputStream("output.txt");int c;// 读取并写入数据,直到达到文件末尾while ((c = in.read()) != -1) {out.write(c);}} finally {if (in != null) {in.close();}if (out != null) {out.close();}}}
}

3.3 Reader与Writer的区别与实践

Reader和Writer则是Java IO库中处理字符流的抽象类。与字节流相比,字符流是用于处理文本数据的。它们运用了装饰器模式,提供了更为复杂的读写操作,如缓冲、过滤、线性、转换等。

import java.io.*;public class TextFileOperations {public static void main(String[] args) throws IOException {BufferedReader reader = null;BufferedWriter writer = null;try {reader = new BufferedReader(new FileReader("input.txt"));writer = new BufferedWriter(new FileWriter("output.txt"));String line;while ((line = reader.readLine()) != null) {writer.write(line);writer.newLine();}} finally {if (reader != null) {reader.close();}if (writer != null) {writer.close();}}}
}

4. 非阻塞IO模型(NIO)

非阻塞IO(NIO)是Java提供的一种比传统阻塞IO(BIO)更高效的IO处理方式。NIO支持面向缓冲区的(Buffer)、基于通道的(Channel)IO操作,并能够提供非阻塞和选择器(Selector)机制,极大地提高了IO操作的性能。

4.1 NIO的概念和特点

NIO的核心在于非阻塞和选择器的概念。非阻塞模式允许线程从某通道读写数据时,即使没有读写数据,也可以立即返回进行其他任务。选择器则允许单个线程同时监控多个输入通道,如果某个通道有数据可读或可写,线程就会转到该通道进行操作。NIO通过这种方式可以使一个单独的线程高效地管理多个并发IO操作。

4.2 NIO的基本组成部分及其关系

NIO的架构包括以下几个核心组件:

  • Channels(通道):类似于流,但有些不同。通道可以同时进行读写操作,并且总是基于Buffer操作数据。
  • Buffers(缓冲区):容器对象,通道用它们与NIO服务交换数据。
  • Selectors(选择器):用于监听多个通道的事件(例如:连接打开、数据到达)。因此,单个的线程可以管理多个通道的IO操作。

下面是一个简单的非阻塞IO数据读写的示例:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;public class NIOSocketClient {public static void main(String[] args) throws IOException {// 打开一个新的socket通道SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false); // 开启非阻塞模式socketChannel.connect(new InetSocketAddress("localhost", 8080));while(!socketChannel.finishConnect()) {// 这里不做任何操作,等待连接完成System.out.println("连接中..."); }String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(48);buf.clear();buf.put(newData.getBytes());buf.flip();while(buf.hasRemaining()) {socketChannel.write(buf); // 写入数据}socketChannel.close(); // 关闭通道}
}

NIO的非阻塞模式使得IO操作可以非常灵活,通道和缓冲区的设计也都是为了提高数据处理的速度。它在处理大量连接,需要高并发的应用场景中展现出极好的性能。

5. JAVA NIO核心组件

JAVA NIO中的核心组件包括Buffers(缓冲区)、Channels(通道)和Selectors(选择器)。这些构造块共同工作,为JAVA提供了一个强大的IO框架。

5.1 Buffer的类型与使用方法

Buffer是NIO中用来存储数据的对象。不同的数据类型可以用不同类型的缓冲区进行处理,比如ByteBuffer、CharBuffer、IntBuffer等。Buffer本身有一系列属性用来表示缓冲区的状态,包括capacity(容量)、position(位置)、limit(限制)和mark(标记)。
一个Buffer的基本用法如下:

import java.nio.ByteBuffer;public class BufferUsage {public static void main(String[] args) {// 分配一个容量为10的ByteBufferByteBuffer buffer = ByteBuffer.allocate(10);printBufferState("After allocation", buffer);// 写入数据到Bufferfor (int i = 0; i < 5; i++) {buffer.put((byte) i);}printBufferState("After putting data", buffer);// 准备读取数据buffer.flip();printBufferState("After flip", buffer);while (buffer.hasRemaining()) {System.out.println(buffer.get());}printBufferState("After read", buffer);// 清空Bufferbuffer.clear();printBufferState("After clear", buffer);}private static void printBufferState(String stage, ByteBuffer buffer) {System.out.println(stage + ": position=" + buffer.position() + ", limit=" + buffer.limit() + ", capacity=" + buffer.capacity());}
}

Buffer的写入、翻转、读取和清空是NIO中非常重要的操作,理解这些操作对于使用NIO至关重要。

5.2 Channel的类型及其与Buffer的交互案例

Channel是对原始操作系统IO操作的一个抽象,并且只能与Buffer一起使用来读写数据。NIO提供了多种通道类型,包括用于文件操作的FileChannel、用于网络操作的SocketChannel、ServerSocketChannel和DatagramChannel。

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;public class ChannelUsage {public static void main(String[] args) throws Exception {RandomAccessFile file = new RandomAccessFile("data.txt", "rw");FileChannel channel = file.getChannel();ByteBuffer buffer = ByteBuffer.allocate(48);// 读取数据到Bufferint bytesRead = channel.read(buffer);while (bytesRead != -1) {buffer.flip(); // 切换模式,写->读while(buffer.hasRemaining()){System.out.print((char) buffer.get());}buffer.clear(); // 清空Buffer,准备再次写入bytesRead = channel.read(buffer);}file.close();}
}

Channel和Buffer一起使用提供了一个强大的数据读写机制。它们让复杂的IO操作变得更加容易。

5.3 Selector的原理与注册通道案例

Selector在NIO中扮演着中心角色,允许单个线程处理多个Channel的IO事件。一个Selector可以注册多个Channel,每当注册的Channel上发生读写事件时,这个Channel就会被Selector捕捉。

import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SelectionKey;public class SelectorUsage {public static void main(String[] args) throws Exception {// 创建一个SelectorSelector selector = Selector.open();// 打开一个通道ServerSocketChannel ssc = ServerSocketChannel.open();ssc.configureBlocking(false); // 设置为非阻塞模式ssc.register(selector, SelectionKey.OP_ACCEPT); // 通道注册到选择器while (true) {// select方法返回值表示有多少通道已经就绪int readyChannels = selector.select();if (readyChannels == 0) continue;// 省略处理就绪通道的逻辑}}
}

6. 多路复用IO模型

多路复用IO模型是一种高效的IO处理方式,它允许单个线程同时监控多个网络连接的IO状态。在Java NIO中,这是通过Selector实现的。这个模型提升了应用程序的性能,尤其是在需要处理大量网络连接的服务器应用程序中。

6.1 多路复用IO简介

在传统的阻塞IO模型中,每个网络连接都需要一个线程去处理,大量的并发连接可能会导致系统过多的线程开销,从而影响性能。而多路复用IO技术通过一种称为"事件通知机制"来允许单个线程管理多个并发连接。

6.2 Selector的高级用法与案例分析

Selector使得单个线程可以监听多个通道的IO事件。当某个事件到来时,线程可以从休眠中唤醒并处理事件。这样做的好处是,同一个线程可以管理多个连接,而不是为每个连接都创建一个线程。

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 MultiplexingIOServer {public static void main(String[] args) throws Exception {// 创建Selector和ChannelSelector selector = Selector.open();ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.bind(new InetSocketAddress("localhost", 8080));serverSocket.configureBlocking(false);serverSocket.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 等待事件if (selector.select(3000) == 0) {continue; // 没有事件}// 获取待处理的事件集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iter = selectedKeys.iterator();while (iter.hasNext()) {SelectionKey key = iter.next();if (key.isAcceptable()) {// 接受客户端连接handleAccept(serverSocket, selector);}if (key.isReadable()) {// 读取客户端数据handleRead(key);}iter.remove();}}}private static void handleAccept(ServerSocketChannel serverSocket, Selector selector) throws IOException {SocketChannel client = serverSocket.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);}private static void handleRead(SelectionKey key) throws IOException {SocketChannel client = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);client.read(buffer);buffer.flip();// 在这里处理buffer中的数据...}
}

这个例子展示了如何设置一个使用Selector的多路复用IO服务器。ServerSocketChannel注册到Selector上,并设置为非阻塞模式。Selector会监听客户端的连接请求,并可以处理多个连接的数据读取。
这种多路复用IO模型提高了网络服务器处理并发连接的能力,对于构建高性能的网络应用程序是非常重要的。

7. 信号驱动IO模型与Java中的体现

信号驱动IO(Signal-driven I/O)模型是UNIX和Linux中支持的一种IO模型,它使用信号通知应用程序何时开始非阻塞IO操作。然而,在标准的Java IO库中,并没有直接提供信号驱动IO,在此我们将讨论其在Java中的体现。

7.1 讨论Java中是否有信号驱动IO

Java语言设计时更注重于跨平台特性,并没有直接支持依赖于特定操作系统的信号机制。因此,在Java标准API中没有直接实现信号驱动IO。然而,在Java的NIO中,通过Selector提供的多路复用能力,在某种程度上可以被看作是类似信号驱动的模型。应用程序可以注册特定事件到Selector上,当事件达成时,应用程序会得到通知。

7.2 对应其他语言的信号驱动IO进行对比

其他一些如C语言基于UNIX/Linux的应用程序可以直接使用操作系统提供的信号驱动IO功能。在Java中,尽管无法直接使用信号机制,但可以通过NIO的Selector等待多个通道事件,这在概念上与信号驱动IO相似,都是基于事件通知机制。
在对性能要求极高的场景下,Java开发者可以通过JNI(Java Native Interface)调用本地代码来实现更贴近操作系统的功能,但这种方法通常不推荐,因为它牺牲了跨平台特性并且可能会引入更多的复杂性和潜在问题。

8. 异步IO模型(AIO)

异步IO(AIO)模型中,应用程序可以立即返回,无需等待IO操作的完成。操作系统会在IO操作完成后通知应用程序,这样应用程序就可以处理其他任务。在Java中,这种模型被称为NIO.2,是从Java 7开始引入的。

8.1 AIO的技术背景

异步IO模型与前面提到的同步和多路复用IO模型有本质的区别。同步IO操作在处理IO请求时,即使是非阻塞的,应用程序也需要主动检查或等待IO操作完成。而在异步IO模型中,操作系统将完成整个IO操作,并在完成后通知应用程序,这极大地提升了应用程序的性能和响应能力。

8.2 Java AIO的API与实战案例

在Java中,异步IO操作是通过java.nio.channels.AsynchronousFileChannel和java.nio.channels.AsynchronousSocketChannel类来实现的。这些类允许你直接在IO操作上进行回调或使用Future模式来处理结果。
以下是使用AsynchronousFileChannel来异步读取文件的例子:

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;public class AsynchronousFileRead {public static void main(String[] args) throws Exception {Path path = Paths.get("example.txt");AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);ByteBuffer buffer = ByteBuffer.allocate(1024);long position = 0;// 异步读取文件内容到bufferFuture<Integer> operation = fileChannel.read(buffer, position);// 在此可以执行其他任务// 等待异步操作完成while (!operation.isDone());// 读取完成,处理数据buffer.flip();while (buffer.hasRemaining()) {System.out.print((char) buffer.get());}fileChannel.close();}
}

这段代码展示了如何使用Java的NIO.2 API进行异步文件读取。利用Future对象,我们可以在操作真正完成前让应用程序继续进行,最终实现非阻塞的IO操作。
异步IO是现代编程中非常有用的工具,对于需要高吞吐量及低延迟IO操作的应用程序来说至关重要。

9. NIO与BIO的性能比较

在Java IO编程中,性能是一个关键的考量因素。BIO和NIO提供了两种不同的IO处理方式,它们各有优缺点,适用于不同的场景。

9.1 各模型的适用场景

BIO,即阻塞IO,适合连接数目比较小且固定的架构,这样可以减少并发线程数和上下文切换的开销,简化程序设计。NIO,即非阻塞IO,适合连接数目多且连接时间短(如聊天服务器),可以提高性能,减少资源消耗。

9.2 实际测试案例与性能分析

要理解BIO和NIO的性能差异,可以通过实际测试案例来分析。在一个高并发测试中,我们可以设置两个服务器:一个使用BIO,一个使用NIO。实验结果通常会显示出,在高并发场景下,NIO服务器相比于BIO服务器能够支持更多的并发连接,并且CPU利用率更低。
以下是一个BIO和NIO在实际应用中性能对比的示例:

// 简化的伪代码,展示思路// BIO服务器
class BIOServer {public void start() {while (true) {Socket clientSocket = serverSocket.accept(); // 阻塞new Thread(() -> handleClient(clientSocket)).start();}}// 处理客户端...
}// NIO服务器
class NIOServer {public void start() {while (true) {int readyChannels = selector.select();if (readyChannels == 0) continue;// 处理准备好的通道...}}// 处理通道中的事件...
}// 性能测试
class PerformanceTest {public static void main(String[] args) {// 启动BIO和NIO服务器// 使用并发工具进行压力测试// 记录和比较结果}
}

实际测试中,我们会发现NIO更适合于需要大量并发连接的应用,而BIO则在简单的、低并发的应用中有更好的表现。

10. NIO在现代框架中的应用

在现代的Java框架中,NIO扮演着十分重要的角色。许多流行的框架,例如Netty,都是基于Java NIO来设计和实现的,以提供更高性能的网络通信能力。

10.1 Netty框架中NIO的实际应用

Netty是一个异步事件驱动的网络应用程序框架,它大量利用了Java NIO的特性来提高其性能。Netty的设计目的是为了快速开发高性能、高可靠性的网络服务器和客户端程序。

// Netty使用示例中的伪代码块// 创建服务端的ServerBootstrap实例
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 使用Nio通道类型.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new MyServerHandler()); // 设置自定义处理器}})// 绑定端口并启动去接收进来的连接.bind(port).sync();

Netty通过提供一套包装过的NIO组件,让开发者能够轻松使用NIO编程,而无需直接和复杂的NIO类库打交道。

10.2 NIO在大型项目中的实践案例

许多大型项目和公司,如Apache Cassandra、Elasticsearch、RocketMQ等,都广泛使用NIO来处理海量的网络连接以及大量的数据传输。这些项目的成功案例证明了NIO在实际应用中的高效性和可靠性。
在这些项目中,NIO通常用于实现自定义的通讯协议、高效的数据序列化和反序列化、各种网络通信场景下的速度优化等。高效的NIO实现是这些能够支持高并发和高吞吐量需求的系统的基石。

11. Java IO/NIO最佳实践

为了充分利用Java IO和NIO,需要遵循一些最佳实践。这些实践可以帮助开发人员编写出更高效、更稳定、更可维护的代码。

11.1 IO/NIO选择的注意事项

决定使用IO还是NIO的关键因素包括并发连接数、数据大小、数据处理复杂度等。对于请求处理时间短、并发需求高的场景,NIO是更好的选择。对于并发连接数较少且需要保持长时间连接的场景,使用传统的BIO可能更为合适。

11.2 性能优化技巧与案例分析

性能优化是IO/NIO使用中的重要方面。例如,可以通过增加缓冲区大小来减少实际的物理读写次数;可以重用Buffers以减少内存分配的开销;合理使用SelectionKey的感兴趣操作集合,避免不必要的选择器唤醒;非阻塞模式下,要注意正确处理读写操作返回值。
以下是一个使用NIO时的性能优化实例:

// 使用缓冲池,重用已经存在的缓冲区ByteBuffer buffer = ByteBuffer.allocateDirect(1024); // 直接在操作系统内存中分配// 读取数据
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {buffer.flip(); // 准备从Buffer中读取while (buffer.hasRemaining()) {//...从Buffer读取数据}buffer.compact(); // 压缩Buffer,为下一次写入数据到Buffer做准备bytesRead = channel.read(buffer);
}// 显式地回收直接缓冲区的内存
((DirectBuffer)buffer).cleaner().clean();

使用直接缓冲区可以节省JVM堆内存,并减少在JVM堆和系统内存之间复制数据的次数。这样可以进一步提高IO操作的效率。我们还应该根据实际情况,通过工具和日志来持续监控和优化系统性能。

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

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

相关文章

C++ | Leetcode C++题解之第108题将有序数组转换为二叉搜索树

题目&#xff1a; 题解&#xff1a; class Solution { public:TreeNode* sortedArrayToBST(vector<int>& nums) {return helper(nums, 0, nums.size() - 1);}TreeNode* helper(vector<int>& nums, int left, int right) {if (left > right) {return nu…

算法学习:快速排序

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f680; 引言&#x1f4cc; 快速排序算法核心思想1. 选择基准值&#xff08;Pivot&#xff09;2. 分区操作&#xff08;Partitioning&#xff09;3. 递归排序子序列 &#x1f4cc; JavaScript 实现1. 快速排序主函数2…

基于Perfetto 解读一帧的生产消费流程 Android >= S Qualcomm

广告 首先帮我朋友打个广告 我们一起在运营一个视频号 感兴趣的可以帮忙点击右边这个小铃铛 铃铛 序 1.这个流程里面的东西如果展开其实是有很多的 内容其实还是比较浅显的 sf处就不贴源码了 关一个Vsync就有的解释 当然笔者在流程上先形成一个思维闭环 2.如有小伙伴需要 笔…

Java方法的递归

Java方法的递归 前言一、递归的概念示例代码示例 二、递归执行过程分析代码示例执行过程图 三、递归练习代码示例按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)递归求 1 2 3 ... 10写一个递归方法&#xff0c;输入一个非负整数&#xff0c;返回组成它的数字之和. …

零基础学Java第二十一天之IIO流之对象流

IO流之对象流 1、对象流 1、理解 将对象写入到文件&#xff0c;将文件里的对象读取到程序中 class ObjectInputStream – 对象输入流 class ObjectOutputStream – 对象输出流 序列化/钝化&#xff1a;程序里的对象 写入到 文件中 反序列化/活化&#xff1a;文件中的对象 读取…

【OpenCV实战】OpenCV实现自动调整亮度和对比度

一,基于局部直方图信息增强算法 对比度受限的自适应直方图均衡化(Contrast Limited Adaptive Histogram Equalization,简称CLAHE)是一种用于图像增强的技术,其原理主要基于自适应直方图均衡化(Adaptive Histogram Equalization,简称AHE)但增加了对比度限制来避免过度放…

uniapp蓝牙打印图片

前言 这是个蓝牙打印图片的功能&#xff0c;业务是打印界面固定的demo范围&#xff0c;这里通过html2canvas插件生成的图片base64&#xff0c;然后图片base64绘制到canvas中去后&#xff0c;获取canvas中的像素信息&#xff0c;然后对像素信息进行一个灰度值处理&#xff0c;灰…

在Linux系统中解决Java生成海报文字乱码和缺少字体文件的问题

在Linux系统中,如果缺少特定的字体文件,可以通过以下几种方法来解决: 1. 安装系统字体包 大多数Linux发行版提供了各种字体包,可以通过包管理器安装这些字体包。例如,在Debian/Ubuntu系统上,可以使用以下命令安装常见的字体包: # 安装基本的字体包 sudo apt-get updat…

Java集合的组内平均值怎么计算

要计算Java集合&#xff08;例如List或Set中的Integer、Double或其他数值类型的对象&#xff09;的组内平均值&#xff0c;我们需要遍历这个集合&#xff0c;累加所有的元素值&#xff0c;然后除以集合的大小&#xff08;即元素的数量&#xff09;。以下是一个详细的步骤说明和…

opencl色域变换,处理传递显存数据

在使用ffmpeg解码后的多路解码数据非常慢&#xff0c;还要给AI做行的加速方式是在显存处理数据&#xff0c;在视频拼接融合产品的产品与架构设计中&#xff0c;提出了比较可靠的方式是使用cuda&#xff0c;那么没有cuda的显卡如何处理呢 &#xff0c;比较好的方式是使用opencl来…

go语言的一些常见踩坑问题

开始之前&#xff0c;介绍一下​最近很火的开源技术&#xff0c;低代码。 作为一种软件开发技术逐渐进入了人们的视角里&#xff0c;它利用自身独特的优势占领市场一角——让使用者可以通过可视化的方式&#xff0c;以更少的编码&#xff0c;更快速地构建和交付应用软件&#…

安卓手机APP开发__网络连接性支持VPN

安卓手机APP开发__网络连接性支持VPN 安卓提供了API给开发者,来创建一个虚拟的私有网络(VPN)的解决方案. 根据这里的介绍,你能知道如何开发和测试你的针对安卓设备的VPN的客户端. 概述 VPN允许设备为了安全地连接网络,而没有物理性的连接在一个网络上. 安卓包括了一个内嵌的…

【无重复字符的最长子串】python,滑动窗口+哈希表

滑动窗口哈希表 哈希表 seen 统计&#xff1a; 指针 j遍历字符 s&#xff0c;哈希表统计字符 s[j]最后一次出现的索引 。 更新左指针 i &#xff1a; 根据上轮左指针 i 和 seen[s[j]]&#xff0c;每轮更新左边界 i &#xff0c;保证区间 [i1,j] 内无重复字符且最大。 更新结…

使用JSDOM安全截断文章HTML内容

在Web开发中&#xff0c;经常需要处理大量的HTML内容&#xff0c;尤其是在展示文章预览、动态加载内容或限制显示长度等场景中。直接截断HTML字符串可能会导致页面布局混乱、样式错误或标签不完整等问题。为了安全地截断HTML内容&#xff0c;我们可以利用jsdom库来解析HTML&…

JVM学习-垃圾回收器(一)

垃圾回收器 按线程数分类 串行垃圾回收器 串行回收是在同一时间段内只允许有一个CPU用于执行垃圾回收操作&#xff0c;此时工作线程被暂停&#xff0c;直至垃圾收集工作结束 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合&#xff0c;串行回收器的性能表…

http和https的区别,怎么免费实现https(内涵教学)

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&#xff0c;因此&…

etcd 和 MongoDB 的混沌(故障注入)测试方法

最近在对一些自建的数据库 driver/client 基础库的健壮性做混沌&#xff08;故障&#xff09;测试, 去验证了解业务的故障处理机制和恢复时长. 主要涉及到了 MongoDB 和 etcd 这两个基础组件. 本文会介绍下相关的测试方法. MongoDB 中的故障测试 MongoDB 是比较世界上热门的文…

AI网络爬虫:批量爬取电视猫上面的《庆余年》分集剧情

电视猫上面有《庆余年》分集剧情&#xff0c;如何批量爬取下来呢&#xff1f; 先找到每集的链接地址&#xff0c;都在这个class"epipage clear"的div标签里面的li标签下面的a标签里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 这个…

速盾:负载均衡能防ddos攻击吗?

负载均衡是一种分布式系统的设计思想&#xff0c;通过将流量分散到多个服务器上&#xff0c;以提高系统的稳定性和可扩展性。然而&#xff0c;负载均衡本身并不能完全防止DDoS攻击&#xff0c;但可以在一定程度上减轻其影响。 DDoS&#xff08;分布式拒绝服务&#xff09;攻击…

【C语言】8.C语言操作符详解(1)

文章目录 1.操作符的分类2.⼆进制和进制转换3.原码、反码、补码4.移位操作符4.1 左移操作符4.2 右移操作符 5.位操作符&#xff1a;&、|、^、~5.1 &&#xff1a;按位与5.2 |&#xff1a;按位或5.3 ^&#xff1a;按位异或5.4 ~&#xff1a;按位取反5.5 例题例题1例题2例…