從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.pswp.cn/web/13891.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13891.shtml
英文地址,請注明出處:http://en.pswp.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例…