You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java String不可变性与对象实例:字符串同步锁问题探讨

关于Java中String对象同步的锁范围问题

这个问题问到点子上了——结合String的常量池特性和不可变性,同步String确实容易踩坑,咱们一步步说清楚:

首先得明确两个核心前提:

  • 字符串字面量(比如"hello")会被JVM存入字符串常量池,相同内容的字面量会指向同一个对象实例;
  • new String("hello")会创建一个新的String对象,除非你调用intern()方法,才会让它指向常量池里的对应实例。

回到你的问题:是否会锁定所有相同内容的String实例?答案分两种情况:

1. 当同步的是字符串字面量或intern()后的String对象时——会锁定所有指向该实例的引用

因为这些String引用本质上指向的是常量池里的同一个对象,而synchronized关键字锁的是对象实例本身,不是字符串内容。举个代码例子:

// s1和s2指向常量池里同一个"test"对象
String s1 = "test";
String s2 = "test";

// 这里锁的是常量池中的"test"实例
synchronized(s1) {
    System.out.println("持有s1锁");
    try { Thread.sleep(3000); } catch (Exception e) {}
}

// 执行到这里时会被阻塞,因为s2和s1是同一个对象
synchronized(s2) {
    System.out.println("持有s2锁");
}

这种情况下,任何其他地方用相同字面量或intern()后的"test"做锁,都会被这个同步块阻塞,哪怕是完全无关的代码模块。

2. 当同步的是new String()创建的对象时——不会锁定其他相同内容的String实例

用new String("test")创建的对象是独立的,哪怕内容和常量池里的一样,也是不同的实例。同步这种对象时,只会锁定当前实例,不会影响其他内容相同但实例不同的String:

String s1 = new String("test");
String s2 = new String("test");

// 锁的是s1这个独立实例
synchronized(s1) {
    System.out.println("持有s1锁");
    try { Thread.sleep(3000); } catch (Exception e) {}
}

// 这里不会被阻塞,因为s2是另一个完全独立的对象
synchronized(s2) {
    System.out.println("持有s2锁");
}

重要提醒:尽量不要用String作为锁对象

用字符串字面量做锁有很大风险:

  • 不同的业务模块如果碰巧用了相同内容的字符串做锁,会意外触发跨模块的锁竞争,导致性能问题甚至死锁;
  • 恶意代码可以通过"test".intern()拿到常量池里的同一个对象,故意加锁阻塞你的正常逻辑。

更安全的做法是使用专门的私有锁对象,比如:

private final Object lock = new Object();

// 用这个私有对象做锁
synchronized(lock) {
    // 业务逻辑
}

内容的提问来源于stack exchange,提问作者klonq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:46:12