深入理解Java中String.intern()方法:從原理到并發控制實踐

深入理解 Java 中 String.intern () 方法:從原理到并發控制實踐

在 Java 開發中,String.intern()方法是一個看似簡單卻蘊含深意的 API。它在字符串常量池管理、內存優化以及并發控制等場景中有著關鍵作用。本文將從底層原理出發,結合實際案例詳細解析intern()方法的作用,并重點探討其在并發控制中確保鎖對象唯一性的經典應用。

一、Java 字符串的存儲機制:常量池與堆的分工

要理解intern()方法,首先需要掌握 Java 中字符串的存儲特性。Java 中的字符串有兩種主要存儲方式:

  1. 字符串常量池(String Constant Pool)

    這是 JVM 為了優化字符串存儲而設計的特殊內存區域,用于存儲字面量字符串(如"abc")和通過intern()方法加入的字符串。常量池的核心作用是復用字符串對象—— 相同內容的字符串在常量池中只會保留一份,避免重復創建相同對象造成的內存浪費。

  2. 堆內存(Heap)

    當通過new String("xxx")方式創建字符串時,JVM 會在堆中創建一個新的字符串對象,同時如果常量池中沒有對應的字面量,會將字面量存入常量池。此時,堆中的對象持有指向常量池字面量的引用。

二、String.intern () 方法的核心作用:確保引用唯一性

intern()是 String 類的一個 native 方法,其官方定義為:

當調用intern()方法時,如果常量池中已經存在一個與當前字符串內容相同的字符串,則返回常量池中該字符串的引用;如果不存在,則將當前字符串加入常量池,并返回當前字符串的引用。

簡單來說,intern()的核心作用是將字符串 “入池” 并返回常量池中唯一的引用,確保 “內容相同的字符串” 在內存中指向同一個對象。

實例驗證:intern () 如何統一引用

我們通過代碼驗證intern()的效果:

public class InternDemo {public static void main(String\[] args) {// 情況1:直接定義字面量,默認入池String s1 = "abc";String s2 = "abc";System.out.println(s1 == s2); // true(引用相同,都指向常量池對象)// 情況2:new創建的字符串對象在堆中       String s3 = new String("abc");String s4 = new String("abc");System.out.println(s3 == s4); // false(堆中兩個不同對象)// 情況3:調用intern()后,引用指向常量池String s5 = s3.intern();String s6 = s4.intern();System.out.println(s5 == s6); // true(均指向常量池的"abc")System.out.println(s5 == s1); // true(與字面量引用一致)}}

輸出結果清晰地展示了intern()的作用:無論堆中創建了多少個內容相同的字符串對象,通過intern()處理后,它們的引用都會指向常量池中唯一的對象。

三、為什么在并發控制中需要 intern ()?

在多線程場景中,synchronized關鍵字是控制并發的常用手段,但其生效的核心前提是鎖對象的引用必須唯一—— 如果兩個線程使用不同的鎖對象(即使內容相同),synchronized將無法保證同步效果,可能導致數據不一致。

以用戶點贊功能為例,假設我們需要通過用戶 ID 作為鎖對象,確保同一用戶的點贊操作串行執行(避免并發導致點贊數錯亂)。如果直接使用userId.toString()作為鎖對象,會存在隱藏風險:

問題場景:未使用 intern () 的鎖失效風險

// 模擬多線程環境下的點贊操作public class ThumbUpService {// 模擬點贊數存儲private Map\<Long, Integer> thumbCountMap = new ConcurrentHashMap<>();public void doThumbUp(Long userId) {// 直接使用userId.toString()作為鎖對象synchronized (userId.toString()) {int currentCount = thumbCountMap.getOrDefault(userId, 0);thumbCountMap.put(userId, currentCount + 1);}}}

上述代碼存在隱患:userId.toString()每次調用都會創建一個新的 String 對象(堆中),即使userId的值相同,兩次調用toString()生成的字符串引用也不同。例如:

Long userId = 10086L;
String lock1 = userId.toString();
String lock2 = userId.toString();
System.out.println(lock1 == lock2); // false(引用不同)

這意味著,同一用戶的兩次點贊操作可能會獲得不同的鎖對象,導致synchronized失效,最終出現點贊數統計錯誤(如并發寫入導致的計數丟失)。

解決方案:用 intern () 確保鎖對象唯一

要解決上述問題,只需對userId.toString()調用intern()方法:

public void doThumbUp(Long userId) {// 使用intern()確保鎖對象唯一synchronized (userId.toString().intern()) {int currentCount = thumbCountMap.getOrDefault(userId, 0);thumbCountMap.put(userId, currentCount + 1);}}

此時,無論userId.toString()創建多少個堆對象,intern()都會返回常量池中唯一的字符串引用。例如:

Long userId = 10086L;
String lock1 = userId.toString().intern();
String lock2 = userId.toString().intern();
System.out.println(lock1 == lock2); // true(引用相同)

這樣一來,同一用戶的所有并發操作都會競爭同一個鎖對象,synchronized能正確保證操作的串行執行,避免數據不一致。

四、源碼解析:intern () 在實際項目中的典型應用

在開篇提到的 “取消點贊” 方法中,intern()的作用正是確保鎖對象唯一:

public Boolean undoThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {User loginUser = userService.getLoginUser(request);// 關鍵:用intern()確保同一用戶的鎖對象唯一synchronized (loginUser.getId().toString().intern()) {// 業務邏輯:取消點贊(更新點贊數、刪除點贊記錄)// ...}}

這里的核心邏輯是:

  • loginUser.getId()返回用戶唯一 ID(如 10086);

  • toString()將 ID 轉為字符串(如 “10086”);

  • intern()確保該字符串在常量池中只有唯一引用。

無論同一用戶觸發多少次 “取消點贊” 操作,synchronized始終會獲取同一個鎖對象,從而保證并發安全。

五、intern () 的其他重要特性與注意事項

  1. 性能與內存考量

    intern()的實現依賴字符串常量池(JDK 7 + 后常量池移至堆中),其底層通常通過哈希表實現,因此intern()的調用本質是一次哈希表的查詢 / 插入操作,時間復雜度為 O (1)(理論上)。但頻繁對大量不同字符串調用intern()可能導致常量池膨脹,需在 “引用唯一性” 和 “內存占用” 間權衡。

  2. 與字符串常量的區別

    字面量字符串(如"abc")默認會入池,而new String("abc")創建的對象需顯式調用intern()才會入池。例如:

String s1 = "abc"; // 直接入池
String s2 = new String("abc").intern(); // 顯式入池,與s1引用相同
  1. JDK 版本兼容性

    雖然intern()的核心邏輯在各 JDK 版本中保持一致,但常量池的存儲位置(JDK 6 及之前在方法區,JDK 7 + 在堆中)和實現細節可能略有差異,不過這對intern()的使用方式和最終效果無影響。

  2. 避免過度使用

    并非所有字符串都需要intern(),僅在需要 “強制復用相同內容的字符串引用” 時使用(如鎖對象、緩存鍵等場景)。

六、總結

String.intern()方法看似簡單,卻深刻體現了 Java 對字符串優化的設計思想。其核心價值在于確保內容相同的字符串在內存中擁有唯一引用,這一特性使其在并發控制中成為保證鎖對象唯一性的關鍵手段。

在實際開發中,理解intern()的原理不僅能幫助我們寫出更安全的并發代碼,還能在字符串優化、內存管理等場景中做出更合理的設計決策。希望本文能讓你對intern()方法有更深入的理解,在面對類似問題時能靈活運用這一強大工具。

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

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

相關文章

在Linux中創建LVGL應用

在Linux中創建LVGL應用 簡介 上一篇文章介紹了在imx6上開發UI的流程 . 這篇接上文&#xff0c; 介紹具體的開發步驟。 1. 創建項目主目錄 mkdir my_lvgl_project cd my_lvgl_project2. 初始化 Git 倉庫 (可選但推薦) git init echo "# My Project with Dependencies&…

大模型對比評測:Qwen2.5 VS Gemini 2.0誰更能打?

一、背景與選型關鍵 在 AI 應用落地的時代&#xff0c;“AI大模型選型對比”成為關鍵環節。選擇合適的模型要綜合考量性能、上下文長度、推理能力、中文/編程支持、成本等多維度指標。 本文重點比較 Gemini2.0Flash-Lite &#xff08;Preview&#xff09;、Gemini2.0Flash &a…

轉置卷積解釋與示例計算

文章目錄轉置卷積的三種等價實現方法&#xff1a;原理、公式與等價性分析數學定義與核心公式方法一&#xff1a;零填充翻轉核卷積&#xff08;數學定義方法&#xff09;原理與公式等價性說明方法二&#xff1a;直接位置映射&#xff08;pytorch框架高效實現&#xff09;原理與公…

關于車位引導及汽車乘梯解決方案的專業性、系統性、可落地性強的綜合設計方案與技術實現說明,旨在為現代智慧停車樓提供高效、安全、智能的停車體驗。

一、系統概述隨著城市土地資源日益緊張&#xff0c;立體停車、自動化停車成為發展趨勢。本方案圍繞“車位引導系統 汽車乘梯系統”構建智慧停車核心體系&#xff0c;結合地磁/視頻/超聲波檢測、AI識別、語音交互、電梯自動調度等先進技術&#xff0c;實現車輛入場、引導、停泊…

【相機】曝光時間長-->拖影

曝光時間長 → 運動目標在快門開啟期間持續移動 → 同一像素記錄多個位置的能量 → 圖像出現“拖影”&#xff08;運動模糊&#xff09;。&#x1f50d; 具體原因卷簾快門&#xff08;Rolling Shutter&#xff09;效應 RealSense 的 RGB 傳感器&#xff08;如 IMX 系列&#xf…

day36 力扣1049.最后一塊石頭的重量II 力扣494.目標和 力扣474.一和零

最后一塊石頭的重量II有一堆石頭&#xff0c;用整數數組 stones 表示。其中 stones[i] 表示第 i 塊石頭的重量。每一回合&#xff0c;從中選出任意兩塊石頭&#xff0c;然后將它們一起粉碎。假設石頭的重量分別為 x 和 y&#xff0c;且 x < y。那么粉碎的可能結果如下&#…

Java內存模型(Java Memory Model,JMM)

?? JMM?? 是Java虛擬機&#xff08;JVM&#xff09;規范中定義的一組規則和規范&#xff0c;用于描述多線程環境下&#xff0c;Java程序中變量的訪問和修改行為&#xff0c;尤其是在并發編程中如何保證內存可見性、原子性和有序性。JMM 是 Java 并發編程的基石&…

【swoole Windows 開發(swoole-cli 開發 hyperf)】

先前swoole在Windows平臺的開發體驗極差&#xff0c;如果在Windows開發swoole的東西可以用docker或者虛擬機&#xff0c;遠程開發&#xff0c;體驗比較好的是直接Mac或者Linux系統開發。但是作為window平臺的釘子戶表示我窮。swoole之前已經推出了cygwin64編譯成winwods版本的方…

興達餐飲 酒店 進銷存管理系統軟件

興達餐飲 酒店 進銷存管理系統軟件

Seal Report:一款免費開源的報表工具

Seal Report 是一款基于 C# 語言開發的開源報表工具&#xff0c;可以從各種數據庫或 NoSQL 數據源中生成日常報告&#xff0c;并且執行復雜的計劃任務。 功能特性 免費開源&#xff1a;源代碼托管在 GitHub 上&#xff0c;用戶可以自由使用、修改、甚至集成到自己的系統中&…

WebRTC 多媒體 SDP 示例與解析

webRTC中的SDP的Bundlle可能包含一個或者多個媒體塊&#xff08;媒體描述, 源碼對應類ContentInfo&#xff09;&#xff0c;從 m 開始到下一個 m 行&#xff08;或 SDP 結束&#xff09;之間的所有屬性&#xff08;包括 a&#xff09;都屬于同一個媒體塊&#xff08;media sect…

SpringBoot 啟動富文本文字更改

正常來說 SpringBoot啟動時候&#xff0c;展示的文字是這個 、 主播這邊想要換一個樣式&#xff0c;換一個自己自定義的文字 這邊換成了自己的博客名字 具體實現操作如下 在項目目錄 resources下創建一個名字為banner.txt的文本&#xff0c;這是SpringBoot啟動的時候尋找的…

基于結構熵權-云模型的鑄鐵浴缸生產工藝安全評價

一、評價模型核心思想 結構熵權法 解決傳統熵權法忽略指標間結構關系的問題,通過指標層次網絡計算權重。 步驟: 構建工藝安全評價指標體系(樹狀/網絡結構) 計算同級指標間的影響度矩陣 引入修正熵權:wj=1?Ej∑(1?Ek)結構影響因子w_j = \frac{1 - E_j}{\sum (1 - E_k)} \…

[Linux]從零開始的vs code交叉調試arm Linux程序教程

一、前言 最近的項目中需要集成rknn的視覺識別&#xff0c;在這之前我并且沒有將rknn集成到自己項目的經驗。這里我需要在rknn原本demo的基礎上我還需要集成自己的業務代碼。但是又有一個問題&#xff0c;原本rknn我們都是使用交叉編譯編譯到開發板上的&#xff0c;并且我們還要…

視頻號私信自動化回復插件

給自己的瀏覽器插件又增加了視頻號斯信的自動化回復搜索&#xff1a;程序員老狼主體邏輯就是&#xff0c;不停的點擊打招呼和斯信那個tab切換查看有無小紅點&#xff0c;有小紅點的會話&#xff0c;就點擊。查看有無打招呼&#xff0c;有打招呼就點擊&#xff0c;抓取昵稱和內容…

Web前端實現銀河粒子流動特效的3種技術方案對比與實踐

文章目錄 前端實現銀河粒子流動特效的技術原理與實踐 引言:銀河粒子特效的技術背景與現狀 技術發展歷史 當前技術現狀 技術原理與實現方案 思維導圖:銀河粒子特效技術架構 1. CSS3實現方案 基礎實現代碼 性能優化技巧 2. Canvas 2D實現方案 基礎實現代碼 Canvas高級優化技術 …

Linux:告別Jammy,擁抱Noble!WSL Ubuntu 22.04 到 24.04 LTS 終極升級指南

大家好&#xff01;如果大家和我一樣&#xff0c;是Windows Subsystem for Linux (WSL) 的忠實用戶&#xff0c;那么大家一定對Ubuntu在其中的表現印象深刻。我們中的許多人可能還在使用穩定可靠的Ubuntu 22.04 LTS (Jammy Jellyfish)。但現在&#xff0c;一個更令人興奮的時代…

江協科技STM32 11-1 SPI通信協議

本節課我們將繼續學習下一個通信協議&#xff0c;SPI。SPI通信和我們剛剛學習過的I2C通信差不多。兩個協議的設計目的都一樣都是實現主控芯片和各種外掛芯片之間的數據交流&#xff0c;有了數據交流的能力&#xff0c;我們的主控芯片就可以掛載并操縱各式各樣的外部芯片&#x…

預過濾環境光貼圖制作教程:第一步 - HDR 轉立方體貼圖

在基于物理的渲染(PBR)中,環境光貼圖是實現真實光照效果的核心組件之一。而將 HDR 全景圖轉換為立方體貼圖,是制作預過濾環境光貼圖的基礎步驟。本教程將詳細講解如何實現這一轉換過程。 什么是 HDR 轉立方體貼圖? HDR(高動態范圍)全景圖通常以等矩形投影(Equirectan…

02 深度學習介紹【動手學深度學習v2】| 學習筆記

1、intro自然語言處理雖然我們過去取得了很大的進展&#xff0c;但是實際上還是停留在感知層面。計算機視覺領域&#xff0c;因為圖片里面都是像素&#xff0c;像素很難用符號學來解釋&#xff0c;所以計算機視覺大部分是用概率模型或機器學習來做。深度學習它是機器學習的一種…