ThreadLocal - 原理與應用場景詳解

ThreadLocal 的基礎概念

在 Java 的多線程世界里,線程之間的數據共享與隔離一直是一個關鍵話題。如果處理不當,很容易引發線程安全問題,比如數據混亂、臟讀等。而 ThreadLocal 這個工具類,就像是為線程量身定制的 “私人儲物柜”,為每個線程提供了獨立的存儲空間,完美地解決了線程間數據隔離的問題。

ThreadLocal 是什么?

ThreadLocal 是 Java 中一個非常實用的類,它為每個線程都提供了自己獨立的變量副本。換句話說,每個線程都可以通過 ThreadLocal 來設置(set)和獲取(get)自己的私有變量,而不會和其他線程產生任何干擾,就像每個線程都有自己的 “小金庫”,互不干擾,互不影響。

舉個簡單的例子,假如我們有一個變量 count,在普通情況下,多個線程同時訪問這個變量時,很容易出現數據混亂的情況,因為它們都操作的是同一個內存地址的變量。但如果我們把 count 放到 ThreadLocal 中,那么每個線程都會有自己獨立的 count 副本,線程 A 對它的 count 副本進行修改,完全不會影響到線程 B 的 count 副本。

ThreadLocal 的基本功能與特點

  • 線程隔離 :這是 ThreadLocal 最顯著的特點。每個線程對 ThreadLocal 變量的讀寫操作都局限在自己的線程內,完全不會與其他線程產生數據共享或沖突。這種線程隔離的特性使得 ThreadLocal 在處理一些需要線程私有數據的場景時非常有用,比如在每個線程中保存獨立的配置信息、用戶身份信息等。
  • 無需顯式加鎖 :由于線程間的數據隔離,使用 ThreadLocal 變量時,不需要像操作共享變量那樣使用顯式的鎖機制(如 synchronizedReentrantLock)來保證線程安全。這大大簡化了多線程編程的復雜度,提高了開發效率。

ThreadLocal 的基礎使用示例

下面通過一個簡單的代碼示例來感受一下 ThreadLocal 的基本使用方式:

public class ThreadLocalExample {// 創建一個ThreadLocal變量private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 在主線程中設置ThreadLocal變量的值threadLocal.set("主線程的值");// 在主線程中獲取ThreadLocal變量的值System.out.println("主線程獲取的值:" + threadLocal.get());// 啟動兩個子線程for (int i = 0; i < 2; i++) {new Thread(() -> {// 子線程設置自己的ThreadLocal變量的值threadLocal.set(Thread.currentThread().getName() + "的值");// 子線程獲取自己的ThreadLocal變量的值System.out.println(Thread.currentThread().getName() + "獲取的值:" + threadLocal.get());// 子線程結束后清理ThreadLocal變量threadLocal.remove();}).start();}// 主線程結束后清理ThreadLocal變量threadLocal.remove();}
}

代碼運行結果示例

主線程獲取的值:主線程的值
Thread-0獲取的值:Thread-0的值
Thread-1獲取的值:Thread-1的值

應用場景概覽

ThreadLocal 在實際開發中有著廣泛的應用場景,以下是一些常見的場景:

1. 線程隔離

在線程池或者其他多線程場景中,我們可以用 ThreadLocal 來存儲每個線程的獨立數據,從而避免多線程共享數據帶來的問題。例如,存儲每個線程的日志信息、用戶身份信息等。

public class UserContextHolder {private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();public static void setUser(User user) {userThreadLocal.set(user);}public static User getUser() {return userThreadLocal.get();}public static void removeUser() {userThreadLocal.remove();}
}// 在線程中使用
public class MyRunnable implements Runnable {@Overridepublic void run() {User user = new User("User-" + Thread.currentThread().getName());UserContextHolder.setUser(user);// 執行業務邏輯System.out.println("當前線程:" + Thread.currentThread().getName() + ",用戶:" + UserContextHolder.getUser().getName());UserContextHolder.removeUser();}
}

場景模擬: 假設我們有一個在線教育平臺,不同的線程代表不同的用戶請求。我們可以通過 ThreadLocal 存儲每個用戶的身份信息,這樣在后續的業務邏輯處理中,就可以方便地獲取當前用戶的信息,而不會和其他線程的用戶信息混在一起。

2. 跨層數據傳遞

在分層架構的系統中,ThreadLocal 可以用來在不同的層之間傳遞數據,而無需在每一層都顯式地傳遞參數。例如,在 Web 開發中,從控制器層到服務層再到數據訪問層,傳遞請求相關的數據。

public class RequestContextHolder {private static final ThreadLocal<RequestData> requestDataThreadLocal = new ThreadLocal<>();public static void setRequestData(RequestData requestData) {requestDataThreadLocal.set(requestData);}public static RequestData getRequestData() {return requestDataThreadLocal.get();}public static void removeRequestData() {requestDataThreadLocal.remove();}
}// 控制器層
@RestController
@RequestMapping("/api")
public class MyController {@PostMapping("/process")public String processRequest(@RequestBody RequestData requestData) {RequestContextHolder.setRequestData(requestData);// 調用服務層myService.process();RequestContextHolder.removeRequestData();return "Request processed successfully";}
}// 服務層
@Service
public class MyService {public void process() {RequestData requestData = RequestContextHolder.getRequestData();// 使用 requestData 進行業務處理System.out.println("Processing request: " + requestData);}
}

場景模擬: 在處理一個 HTTP 請求時,我們可以在控制器層將請求的相關數據(如請求 ID、用戶身份信息等)存儲到 ThreadLocal 中。然后在服務層和數據訪問層,就可以直接從 ThreadLocal 中獲取這些數據,而無需在每一層都顯式地傳遞參數。這大大簡化了代碼邏輯,提高了開發效率。

3. 復雜調用鏈路的全局參數傳遞

在復雜的調用鏈路中,比如分布式系統中的請求跟蹤、日志記錄等場景,ThreadLocal 可以用來在整個調用鏈中保持某些參數的連續性。

public class TraceContextHolder {private static final ThreadLocal<String> traceIdThreadLocal = new ThreadLocal<>();public static void setTraceId(String traceId) {traceIdThreadLocal.set(traceId);}public static String getTraceId() {return traceIdThreadLocal.get();}public static void removeTraceId() {traceIdThreadLocal.remove();}
}// 在入口處設置 Trace ID
public class ApiGatewayFilter implements GenericFilterBean {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {String traceId = UUID.randomUUID().toString();TraceContextHolder.setTraceId(traceId);try {chain.doFilter(request, response);} finally {TraceContextHolder.removeTraceId();}}
}// 在后續的服務調用中使用 Trace ID
public class MyService {public void process() {String traceId = TraceContextHolder.getTraceId();// 使用 traceId 進行日志記錄等操作System.out.println("Processing with traceId: " + traceId);}
}

場景模擬: 在一個分布式系統中,當一個請求進入系統時,我們在入口處(如 API 網關)生成一個唯一的 Trace ID,并將其存儲到 ThreadLocal 中。在后續的各個服務調用中,都可以從 ThreadLocal 中獲取這個 Trace ID,用于日志記錄、請求跟蹤等操作。這樣可以方便地追蹤一個請求在整個系統中的流轉路徑,便于問題排查和性能分析。

4. 數據庫連接的管理

在涉及到數據庫連接的嵌套調用場景中,ThreadLocal 可以用來確保每個線程都有自己的數據庫連接,避免連接共享帶來的問題,保證事務的一致性。

public class DBContextHolder {private static final ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();public static void setConnection(Connection connection) {connectionThreadLocal.set(connection);}public static Connection getConnection() {return connectionThreadLocal.get();}public static void removeConnection() {connectionThreadLocal.remove();}
}// 在數據訪問層獲取數據庫連接
public class MyDAO {public void executeQuery(String sql) {Connection connection = null;try {connection = DBContextHolder.getConnection();if (connection == null) {connection = dataSource.getConnection();DBContextHolder.setConnection(connection);}// 執行 SQL 查詢System.out.println("Executing query: " + sql + " on connection: " + connection.hashCode());} catch (SQLException e) {e.printStackTrace();} finally {// 在實際開發中,連接的關閉可能需要根據具體情況處理// DBContextHolder.removeConnection();}}
}

場景模擬: 在 AOP(面向切面編程)場景中,當我們進行數據庫操作時,可以通過 ThreadLocal 來管理數據庫連接。在事務的開始階段,獲取一個數據庫連接并存儲到 ThreadLocal 中。在后續的多個數據庫操作中,都可以從 ThreadLocal 中獲取這個連接,確保所有的操作都在同一個數據庫連接上執行,從而保證事務的一致性。

總結

ThreadLocal 的應用場景非常豐富,它在實現線程隔離、跨層數據傳遞、復雜調用鏈路的全局參數傳遞以及數據庫連接管理等方面都有著獨特的價值。通過這些實際的應用場景,我們可以看到 ThreadLocal 在簡化多線程編程復雜度、提高代碼可維護性方面的重要作用。在接下來的章節中,我們將深入探討 ThreadLocal 的工作原理,進一步加深對其的理解。

以上是 ThreadLocal 的應用場景概覽,希望這些內容能幫助你更好地理解和使用 ThreadLocal。如果你有任何問題或想法,歡迎隨時交流!

ThreadLocal 的原理剖析

了解了 ThreadLocal 的應用場景后,現在我們來深入探討一下它的工作原理。

ThreadLocalMap 的內部構造

ThreadLocal 的核心在于每個線程內部維護的一個名為 ThreadLocalMap 的映射表。這個映射表存儲了線程本地變量的鍵值對,其中鍵是 ThreadLocal 對象本身,值則是線程本地變量的具體值。

  • ThreadLocalMap 的結構
    • ThreadLocalMapThreadLocal 的一個內部類,它不是一個可以直接公開訪問的數據結構。它的設計目的是為了高效地存儲和檢索線程本地變量。
    • 每個 ThreadLocalMap 實例都包含一個數組 Entry[],該數組的元素是 Entry 類型,Entry 是一個靜態內部類,它存儲了鍵值對(ThreadLocal 對象和對應的值)。
  • get 和 set 方法的實現
    • get 方法 :當調用 ThreadLocalget 方法時,首先獲取當前線程,然后通過線程獲取其內部的 threadLocals(即 ThreadLocalMap 實例)。如果 ThreadLocalMap 存在,則在其中查找當前 ThreadLocal 對應的值。查找過程是通過 ThreadLocal 對象的哈希值來確定其在 Entry 數組中的位置,進而找到對應的值。如果找不到對應的值,則調用 initialValue 方法進行初始化。
    • set 方法 :當調用 ThreadLocalset 方法時,同樣先獲取當前線程的 ThreadLocalMap。如果 ThreadLocalMap 不存在,則創建一個新的 ThreadLocalMap。然后在 ThreadLocalMap 中查找當前 ThreadLocal 對應的 Entry,如果存在,則更新其值;如果不存在,則創建一個新的 Entry 并將其添加到 ThreadLocalMap 中。

下面是一個簡化的 getset 方法的代碼示例:

public T get() {Thread currentThread = Thread.currentThread();ThreadLocalMap threadLocalMap = currentThread.threadLocals;if (threadLocalMap != null) {ThreadLocalMap.Entry entry = threadLocalMap.getEntry(this);if (entry != null) {return (T) entry.value;}}return setInitialValue();
}private T setInitialValue() {T value = initialValue();Thread currentThread = Thread.currentThread();ThreadLocalMap threadLocalMap = currentThread.threadLocals;if (threadLocalMap != null) {threadLocalMap.set(this, value);} else {currentThread.threadLocals = new ThreadLocalMap(this, value);}return value;
}public void set(T value) {ThreadLocalMap threadLocalMap = Thread.currentThread().threadLocals;if (threadLocalMap != null) {threadLocalMap.set(this, value);} else {createThreadLocalMap(value);}
}private void createThreadLocalMap(T value) {Thread currentThread = Thread.currentThread();currentThread.threadLocals = new ThreadLocalMap(this, value);
}

ThreadLocal 在子線程中的局限性

雖然 ThreadLocal 在線程隔離方面表現得非常出色,但它也有一個明顯的局限性:子線程無法直接獲取父線程中的 ThreadLocal 變量值。
案例演示

public class ThreadLocalInheritanceIssue {private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {threadLocal.set("Main Thread Value");new Thread(() -> {System.out.println("Child Thread Value: " + threadLocal.get());}).start();}
}

運行結果

Child Thread Value: null

結果分析

  • 在主線程中,我們設置了 ThreadLocal 變量的值為 “Main Thread Value”。
  • 然后啟動了一個子線程,在子線程中嘗試獲取 ThreadLocal 變量的值,結果卻是 null。這表明子線程無法直接訪問父線程中的 ThreadLocal 變量值。

為了解決子線程無法獲取父線程 ThreadLocal 變量值的問題,Java 提供了 InheritableThreadLocal 類。InheritableThreadLocalThreadLocal 的一個子類,它允許子線程繼承父線程的線程本地變量值。

InheritableThreadLocal 的實現原理

InheritableThreadLocalThreadLocal 的一個子類,它允許子線程繼承父線程的線程本地變量值。這個特性在某些場景下非常有用,比如在父子線程需要共享某些配置信息時。

  • 繼承機制的工作原理
    • 當子線程通過 new Thread() 的方式創建時,InheritableThreadLocal 會將父線程的 ThreadLocalMap 中的鍵值對復制一份給子線程。這樣,子線程就可以訪問到父線程的線程本地變量值。
    • 但是,如果子線程是從線程池中獲取的(即線程復用的情況),InheritableThreadLocal 將無法正常工作,因為線程池中的線程已經被復用多次,不可能每次都重新復制父線程的 ThreadLocalMap
  • 適用場景與局限性
    • InheritableThreadLocal 適用于需要父子線程共享線程本地變量值的場景,例如在某些需要傳遞線程上下文信息的多線程任務中。
    • 然而,它的局限性在于線程池場景。由于線程池中的線程會被復用,InheritableThreadLocal 無法保證子線程能夠正確繼承父線程的線程本地變量值。為了解決這個問題,可以考慮使用其他擴展方案,例如阿里巴巴開源的 TransmittableThreadLocal

InheritableThreadLocal 的實現原理

  • 當子線程通過 new Thread() 的方式創建時,InheritableThreadLocal 會將父線程的 ThreadLocalMap 中的鍵值對復制一份給子線程。這樣,子線程就可以訪問到父線程的線程本地變量值。
  • 但是,如果子線程是從線程池中獲取的(即線程復用的情況),InheritableThreadLocal 將無法正常工作,因為線程池中的線程已經被復用多次,不可能每次都重新復制父線程的 ThreadLocalMap

代碼示例

public class InheritableThreadLocalExample {private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("Main Thread Value");new Thread(() -> {System.out.println("Child Thread Value: " + inheritableThreadLocal.get());}).start();}
}

運行結果

Child Thread Value: Main Thread Value

結果分析

  • 在主線程中,我們使用 InheritableThreadLocal 設置了線程本地變量的值為 “Main Thread Value”。

  • 啟動的子線程通過 InheritableThreadLocal 成功地繼承了主線程的線程本地變量值,并正確輸出了該值。

雖然 InheritableThreadLocal 解決了子線程繼承父線程 ThreadLocal 變量值的問題,但它在使用線程池的場景下存在局限性。由于線程池中的線程會被復用,InheritableThreadLocal 無法保證子線程能夠正確繼承父線程的線程本地變量值。

案例演示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class InheritableThreadLocalIssue {private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("Main Thread Value");ExecutorService executorService = Executors.newFixedThreadPool(1);// 第一次提交任務executorService.execute(() -> {System.out.println("First Task - Child Thread Value: " + inheritableThreadLocal.get());});try {Thread.sleep(1000); // 確保第一個任務執行完成} catch (InterruptedException e) {e.printStackTrace();}// 第二次提交任務executorService.execute(() -> {System.out.println("Second Task - Child Thread Value: " + inheritableThreadLocal.get());});executorService.shutdown();}
}

運行結果

First Task - Child Thread Value: Main Thread Value
Second Task - Child Thread Value: null

結果分析

  • 在主線程中,我們使用 InheritableThreadLocal 設置了線程本地變量的值為 “Main Thread Value”。
  • 第一次提交的任務成功獲取到了主線程的線程本地變量值。
  • 第二次提交的任務卻返回了 null,這是因為線程池中的線程被復用了,第二次提交的任務并沒有繼承主線程的線程本地變量值。

為了解決這個問題,阿里巴巴開源了 TransmittableThreadLocal 庫。TransmittableThreadLocal 通過在子線程中復制父線程的 ThreadLocal 值,并在線程池任務執行前后進行清理,確保了線程本地變量的正確傳遞和隔離。

(三)TransmittableThreadLocal 的介紹

TransmittableThreadLocal 是阿里巴巴開源的一個擴展庫,它可以解決線程池場景下線程本地變量的傳遞問題。它通過在子線程中復制父線程的 ThreadLocal 值,并在線程池任務執行前后進行清理,確保了線程本地變量的正確傳遞和隔離。

使用示例

引入依賴

<dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.14.3</version>
</dependency>

代碼示例

import com.alibaba.transmittable-thread-local.TransmittableThreadLocal;public class TTLExample {private static final TransmittableThreadLocal<String> TTL = new TransmittableThreadLocal<>();public static void main(String[] args) {TTL.set("Main Thread Value");ExecutorService executorService = Executors.newSingleThreadExecutor();executorService.execute(() -> {System.out.println("Child Thread Value: " + TTL.get());TTL.remove();});executorService.shutdown();}
}

總結

ThreadLocal 的工作原理主要依賴于每個線程內部維護的 ThreadLocalMap,它通過哈希表的方式存儲線程本地變量的鍵值對。InheritableThreadLocal 提供了父子線程之間的變量繼承機制,但在使用時需要注意其局限性。對于線程池場景下的變量傳遞問題,可以借助 TransmittableThreadLocal 等擴展庫來解決。

通過深入理解這些原理,我們能夠更好地在實際開發中應用 ThreadLocal 及其相關擴展,解決多線程環境下的數據隔離和共享問題。接下來,我們將在實際應用案例中進一步驗證這些原理。

以上是關于 ThreadLocal 原理剖析的詳細介紹,希望可以幫助讀者更好地理解其內部工作機制。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76542.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76542.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76542.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iwebsec靶場 文件包含關卡通關筆記11-ssh日志文件包含

目錄 日志包含 1.構造惡意ssh登錄命令 2.配置ssh日志開啟 &#xff08;1&#xff09;配置sshd &#xff08;2&#xff09;配置rsyslog &#xff08;3&#xff09;重啟服務 3.寫入webshell木馬 4.獲取php信息滲透 5.蟻劍連接 日志包含 1.構造惡意ssh登錄命令 ssh服務…

Diamond軟件的使用--(4)搭建Modelsim仿真庫

使用Modelsim仿真的原因 由于diamond自帶的仿真軟件Active-HDL需要另一套Lisence&#xff0c;所以我們使用第三方仿真軟件Modelsim來進行仿真。 Modelsim10.5下載鏈接如下&#xff1a;https://pan.baidu.com/s/1G9699ocWm1UTqK2yS2igyQ 提取碼&#xff1a;lewa 一、Lattice仿…

2025年4月19日,四月第三周,C++,字符串數組答案解析

答案與解析 1. 輸出字符串數組所有元素 cpp 復制 下載 #include <iostream> using namespace std;int main() {string arr[] = {"apple", "banana", "cherry"};int n = sizeof(arr)/sizeof(arr[0]); // 計算數組長度for (int i = 0; …

C語言之高校學生信息快速查詢系統的實現

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 總有人間一兩風&#xff0c;填我十萬八千夢。 &#x1f680; 路漫漫其修遠兮&#xff0c;吾將上下而求索。 C語言之高校學生信息快速查詢系統的實現 目錄 任務陳述與分析 問題陳述問題分析 數據結構設…

【網絡篇】TCP vs UDP底層區別+網絡編程概念

大家好呀 我是浪前 今天講解的是網絡篇的第三章&#xff1a;網絡編程概念和TCP&UDP的區別 網絡編程概念TCP和UDP的區別 跨主機通信:網絡編程插座&#xff1a;網絡編程的本質&#xff1a; 網絡編程的重要概念&#xff1a;客戶端和服務器&#xff1a; 客戶端和服務器的交互模…

EMIF詳解

一、EMIF的基本定義 EMIF&#xff08;External Memory Interface&#xff0c;外部存儲器接口&#xff09; 是嵌入式處理器&#xff08;如DSP、FPGA、SoC&#xff09;用于連接外部存儲器的專用硬件接口模塊&#xff0c;負責管理處理器與存儲器之間的地址/數據總線、控制信號及時…

Keil MDK 編譯問題:function “HAL_IncTick“ declared implicitly

問題與處理策略 問題描述 ..\..\User\stm32f1xx_it.c(141): warning: #223-D: function "HAL_IncTick" declared implicitlyHAL_IncTick(); ..\..\User\stm32f1xx_it.c: 1 warning, 0 errors問題原因 在 stm32f1xx_it.c 文件中調用了 HAL_IncTick()&#xff0c;但…

Java Web項目(一)

框架 java web項目總工分為兩部分&#xff1a;客戶端&#xff08;前端&#xff09;和服務端&#xff08;后端&#xff09; 客戶端發起請求&#xff0c;服務端接受請求并進行處理 發起請求的方式&#xff1a;from表單、jQuery ajax from表單 造成全局的變化&#xff0c;在發…

Dify部署內網時遇到的代理問題及解決辦法

大家知道&#xff0c;在公網環境下利用docker安裝dify源碼鏡像比較容易&#xff0c;詳見我之前的文章&#xff0c;基于dify開發agent、workflow等非常方便&#xff0c;本次想著在內部網絡環境下也完成部署&#xff0c;以方便更多的人使用&#xff0c;但在部署到內網環境下&…

多節點監控的docker管理面板Portainer安裝指南:家庭云計算專家

背景 Portainer 是一個輕量級且功能強大的容器管理面板&#xff0c;專為 Docker 和 Kubernetes 環境設計。它通過直觀的 Web 界面簡化了容器的部署、管理和監控&#xff0c;即使是非技術用戶也能輕松上手。Portainer 支持多節點管理&#xff0c;允許用戶從一個中央控制臺管理多…

Linux內核哈希表學習筆記

前沿 近期項目中需要給自定義的驅動增加一個功能存儲相關的數據信息。結合實際業務層面,最終決定采用哈希表的結構來存儲。因為其具備快速查找,插入和刪除。其實現原理通過散列函數映射到指定位置。時間復雜度O(1).而且運算速度也快,很適合處理大量的數據場景。但是其也有一…

對于在線教育或知識付費類網站視頻處理方案

一、視頻格式&#xff1a; 1. 推薦格式&#xff1a;HLS&#xff08;HTTP Live Streaming&#xff09; 優勢?&#xff1a; ?自適應碼率?&#xff1a;根據用戶網絡狀況自動切換清晰度&#xff0c;避免卡頓。?廣泛兼容性?&#xff1a;iOS/macOS 原生支持&#xff0c;Android…

Deepseek輸出的內容如何直接轉化為word文件?

我們有時候會直接利用deepseek翻譯別人的文章或者想將deepseek輸出的內容直接復制到word文檔里。但是文本格式和word是不對應的。這時候需要輸入如下命令&#xff1a; 以上翻譯內容的格式和排版要求如下&#xff1a; 1、一級標題 字體為黑體&#xff08;三號&#xff09;&…

【Vue】組件通信(Props/Emit、EventBus、Provide/Inject)

個人主頁&#xff1a;Guiat 歸屬專欄&#xff1a;Vue 文章目錄 1. Props/Emit 父子組件通信1.1 Props 向下傳遞數據1.2 Emit 向上傳遞事件 2. EventBus 跨組件通信2.1 創建事件總線2.2 使用事件總線2.3 EventBus 優缺點 3. Provide/Inject 深層組件通信3.1 基本使用3.2 響應式處…

vulnhub sunset系列靶機合集(部分)

描述&#xff1a;該合集包含sunset系列適合新手的四個靶機&#xff08;sunset:1、dusk、sunrise、noontide&#xff09;的滲透全過程。 靶機下載地址&#xff1a;Vulnerable By Design - Search: sunset ~ VulnHubhttps://www.vulnhub.com/?qsunset sunset:1 滲透過程 信息…

【MySQL】MySQL的基礎語法及其語句的介紹

1、基礎語法 mysql -h【主機名】 -u【用戶名】 -p //登錄MySQL exit或quit; //退出MySQL show database; //查看MySQL下的所有數據庫 use 【數據庫名】; //進入數據庫 show tables; //查看數據庫下的所有表名 *MySQL的啟動和關閉 &am…

2025-4-20-C++ 學習 數組(1)

數組 2025-4-20-C++ 學習 數組(1)P1428 小魚比可愛題目描述輸入格式輸出格式輸入輸出樣例 #1輸入 #1輸出 #1說明/提示題解代碼P1427 小魚的數字游戲題目描述輸入格式輸出格式輸入輸出樣例 #1輸入 #1輸出 #1說明/提示數據規模與約定題解代碼P5727 【深基5.例3】冰雹猜想題目描…

ESP-ADF外設子系統深度解析:esp_peripherals組件架構與核心設計(顯示輸出類外設之LCD)

目錄 ESP-ADF外設子系統深度解析&#xff1a;esp_peripherals組件架構與核心設計&#xff08;顯示輸出類外設之LCD&#xff09;簡介模塊概述功能定義架構位置核心特性 LCD外設分析LCD外設概述LCD外設層次架構圖 LCD外設API和數據結構外設層API公共API內部數據結構 LCD外設配置選…

面試題:循環引用兩個節點相互引用,如何判斷哪個用 shared_ptr?哪個用 weak_ptr?

目錄 1.引言 2.原理 3.所有權模型與指針選擇 4.復雜場景的決策策略 5.注意事項 6.總結 1.引言 當兩個對象通過 shared_ptr 相互引用時&#xff0c;會產生循環引用問題&#xff0c;導致內存泄漏。因為這兩個對象的引用計數永遠不會變為 0&#xff0c;即使它們在程序的其他…

QT聊天項目DAY06

1.從git上同步項目 編譯測試&#xff0c;編譯通過 Post請求測試 測試成功 2. email is 打印有問題&#xff0c;檢查 解析結果是存儲在jsonResult中的&#xff0c;修改 3. 客戶端實現Post驗證碼請求 3.1 同步Qt客戶端項目 檢查QT版本&#xff0c;由于我在公司用的還是QT5.12.9…