synchronized可以锁字符串吗?
本文最后更新于 2025-01-13,转载请标明原作者!
synchronized锁的是对象,而非值,所以synchronized是不能直接锁字符串的,例如:
public void test(String qq){
synchronized (qq){
...
}
}
这段代码本意是想锁住同一QQ号码的代码块,但是每次传过来的字符串都是不同的字符串对象,synchronized 就失去了意义。
而如果想要锁住同一QQ,就需要使每次传过来的QQ是同一字符串对象。
方案一
将传过来的QQ放入常量池。
public void test(String qq){
synchronized (qq.intern()){
...
}
}
nameObj.intern()把字符串对象放入常量池中。通过将传过来的QQ放入常量池中,这样多线程并发调用传入相同的QQ时,实际使用同一个常量池中的对象作为锁对象,保证了同步的线程安全性。
synchronized 锁字符串用intern()存在的问题
常量池大小依赖于服务器内存,且回收垃圾只依赖于fullGC,如果数据过大,则很容易造成服务器频繁fullGC,所以不建议直接使用intern()。
方案二
使用google的guava包的interner类
需要导入google的guava包:
Maven:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.1-jre</version>
<!-- or, for Android: -->
<version>33.3.1-android</version>
</dependency>
Gradle:
dependencies {
// Pick one:
// 1. Use Guava in your implementation only:
implementation("com.google.guava:guava:33.3.1-jre")
// 2. Use Guava types in your public API:
api("com.google.guava:guava:33.3.1-jre")
// 3. Android - Use Guava in your implementation only:
implementation("com.google.guava:guava:33.3.1-android")
// 4. Android - Use Guava types in your public API:
api("com.google.guava:guava:33.3.1-android")
}
示例代码:
public class Test(){
private static Interner<String> lock = Interners.newWeakInterner();
public void test(String qq){
synchronized (lock.intern(qq)){
...
}
}
}
Interner是通过MapMaker构造ConcurrentMap来实现弱引用,ConcurrentMap用分段的方式保证安全。比常量池的优点就在于这里是弱引用的方式,便于map的回收,常量池只能依赖于fullGC,这里的回收在不使用或内存不够用条件下即可被回收(Minor GC阶段)。
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员七字节
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果