JAVA中Synchronized能否加鎖字符串詳解
1、簡(jiǎn)述
在 Java 開(kāi)發(fā)中,synchronized 是一種常見(jiàn)的同步機(jī)制,用于保證線(xiàn)程安全。但是你有沒(méi)有思考過(guò)這樣一個(gè)問(wèn)題:
“synchronized 可以給字符串(String)加鎖嗎?”
答案是:可以,但你應(yīng)該非常小心。
本文將深入剖析這個(gè)問(wèn)題,講清楚背后的機(jī)制、風(fēng)險(xiǎn),并給出實(shí)際建議。

2、synchronized 本質(zhì)上加的是什么鎖?
synchronized 實(shí)際上加的是對(duì)象鎖,也叫監(jiān)視器鎖(monitor lock)。也就是說(shuō):
synchronized (obj) {
// 臨界區(qū)
}
這段代碼表示:只有獲取到 obj 這個(gè)對(duì)象的監(jiān)視器鎖的線(xiàn)程才能進(jìn)入臨界區(qū)。
因此,只要是一個(gè)對(duì)象,包括字符串實(shí)例,理論上都可以被用作加鎖對(duì)象。
3、加鎖字符串——看似可行,實(shí)則隱患巨大
來(lái)看一個(gè)例子:
public class StringLockExample {
public void doSomething(String lock) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 獲得了鎖:" + lock);
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {}
}
}
}
啟動(dòng)多個(gè)線(xiàn)程調(diào)用:
StringLockExample example = new StringLockExample();
Runnable task1 = () -> example.doSomething("LOCK");
Runnable task2 = () -> example.doSomething("LOCK");
new Thread(task1).start();
new Thread(task2).start();
結(jié)果是,兩個(gè)線(xiàn)程會(huì)串行執(zhí)行,因?yàn)榧拥氖峭粋€(gè)字符串 "LOCK" 的鎖。
但問(wèn)題來(lái)了:
字符串是不可變對(duì)象,且 JVM 對(duì)字符串常量具有“字符串池”優(yōu)化機(jī)制(String Interning)!
也就是說(shuō):
String a = "LOCK"; String b = "LOCK"; System.out.println(a == b); // true
兩個(gè)字符串變量實(shí)際上引用的是同一個(gè)對(duì)象。
因此,在你以為傳進(jìn)來(lái)的是不同的字符串時(shí),可能實(shí)際上加的是同一把鎖,或者反過(guò)來(lái)——你以為加的是同一把鎖,其實(shí)不是!
4、字符串加鎖的兩個(gè)典型陷阱
4.1 鎖粒度無(wú)法控制
如果你的鎖是這樣定義的:
synchronized ("user_" + userId)
你以為這是“每個(gè)用戶(hù)一個(gè)鎖”,但實(shí)際上由于字符串拼接會(huì)創(chuàng)建新對(duì)象,每次拼接都是一個(gè)新對(duì)象,鎖根本不會(huì)生效。
除非你手動(dòng) .intern() :
synchronized (("user_" + userId).intern())
這又引入了新的問(wèn)題:intern 的對(duì)象存儲(chǔ)在字符串常量池中,頻繁使用可能會(huì)增加內(nèi)存壓力,甚至引發(fā)性能問(wèn)題。
4.2 外部可控鎖對(duì)象
如果你用外部傳入的字符串作為鎖對(duì)象,那你根本無(wú)法控制到底加的是什么鎖。惡意或不規(guī)范調(diào)用者可能傳入一個(gè)常量字符串、空字符串、甚至 null,導(dǎo)致同步行為混亂或拋出異常。
5、安全的替代方案
? 使用自定義鎖對(duì)象
最推薦的方式是自己定義一套鎖策略,例如使用 ConcurrentHashMap 管理鎖對(duì)象:
private final ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
public void doSomething(String key) {
Object lock = lockMap.computeIfAbsent(key, k -> new Object());
synchronized (lock) {
// 臨界區(qū)
}
}
這種方式可以保證每個(gè)業(yè)務(wù) key 對(duì)應(yīng)一個(gè)明確的鎖對(duì)象,而且不會(huì)誤用常量字符串,鎖粒度清晰可控。
6、使用 Google Guava 的 Interner 實(shí)現(xiàn)更安全的字符串鎖
Interner 是 Google Guava 提供的一個(gè)實(shí)用工具類(lèi),用于實(shí)現(xiàn)“字符串實(shí)例的唯一化”。它的作用類(lèi)似于 String.intern(),但更靈活、可控,不依賴(lài)字符串常量池,避免了 JVM 層級(jí)的內(nèi)存污染和性能隱患。
引入依賴(lài):
<!-- Maven --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.1-jre</version> </dependency>
使用示例:
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
public class GuavaInternerLock {
private static final Interner<String> interner = Interners.newWeakInterner();
public void doWork(String key) {
String internedKey = interner.intern(key);
synchronized (internedKey) {
// 同樣 key 的線(xiàn)程會(huì)同步執(zhí)行
System.out.println("Processing key: " + key);
}
}
}
Guava Interner 的優(yōu)勢(shì)
- 不污染 JVM 的字符串常量池(不像
String.intern())。 - 可以選擇 Weak 或 Strong 引用,避免內(nèi)存泄漏。
- 適合在緩存、去重、分布式任務(wù)分片等場(chǎng)景中鎖定“邏輯鍵”。
7、總結(jié)
并發(fā)編程中,鎖不是萬(wàn)能的,濫用鎖更是災(zāi)難。本文完整地分析了:
synchronized是否能加鎖字符串(可以,但不推薦);- 字符串常量池帶來(lái)的鎖隱患;
- 如何使用
Object、ConcurrentHashMap構(gòu)建安全鎖; - 如何用 Guava 的
Interner提供高效、可控的鎖機(jī)制; - 方法參數(shù)中加鎖字符串的風(fēng)險(xiǎn)及解決方案。
寫(xiě)高質(zhì)量的并發(fā)代碼,關(guān)鍵是理解鎖的語(yǔ)義、作用域和生命周期。希望這篇文章能幫你在并發(fā)之路上走得更穩(wěn)更遠(yuǎn)。
到此這篇關(guān)于JAVA中Synchronized能否加鎖字符串的文章就介紹到這了,更多相關(guān)JAVA Synchronized加鎖字符串內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 生成有序賬號(hào)的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇java 生成有序賬號(hào)的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
java 實(shí)現(xiàn)MD5加密算法的簡(jiǎn)單實(shí)例
這篇文章主要介紹了java 實(shí)現(xiàn)MD5加密算法的簡(jiǎn)單實(shí)例的相關(guān)資料,這里提供實(shí)例幫助大家應(yīng)用這樣的加密算法,需要的朋友可以參考下2017-09-09
Spring?Boot項(xiàng)目中遇到`if-else`語(yǔ)句七種具體使用方法解析
當(dāng)在Spring?Boot項(xiàng)目中遇到大量if-else語(yǔ)句時(shí),優(yōu)化這些代碼變得尤為重要,因?yàn)樗鼈儾粌H增加了維護(hù)難度,還可能影響應(yīng)用程序的可讀性和性能,以下是七種具體的方法,用于在Spring?Boot項(xiàng)目中優(yōu)化和重構(gòu)if-else語(yǔ)句,感興趣的朋友一起看看吧2024-07-07
探索Java中的equals()和hashCode()方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了探索Java中的equals()和hashCode()方法的相關(guān)資料,需要的朋友可以參考下2017-05-05
利用Spring Social輕松搞定微信授權(quán)登錄的方法示例
這篇文章主要介紹了利用Spring Social輕松搞定微信授權(quán)登錄的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
springboot應(yīng)用中使用過(guò)濾器的過(guò)程詳解
過(guò)濾器通常用于實(shí)現(xiàn)跨切面的功能,例如身份驗(yàn)證、日志記錄、請(qǐng)求和響應(yīng)的修改、性能監(jiān)控等,這篇文章主要介紹了springboot應(yīng)用中使用過(guò)濾器,需要的朋友可以參考下2023-06-06
java實(shí)現(xiàn)Excel高性能異步導(dǎo)出的完整方案詳解
在大型電商系統(tǒng)中,數(shù)據(jù)導(dǎo)出是一個(gè)高頻且重要的功能需求,本文將設(shè)計(jì)實(shí)現(xiàn)一套完整的Excel異步導(dǎo)出機(jī)制,通過(guò)注解驅(qū)動(dòng)、任務(wù)隊(duì)列、定時(shí)調(diào)度、消息通知等技術(shù)手段,完美解決了大數(shù)據(jù)量導(dǎo)出的技術(shù)難題,成為項(xiàng)目的重要技術(shù)亮點(diǎn)2025-10-10

