String.intern() 线程安全保证吗?

6
是否有记载保证String.intern()是线程安全的?Java文档暗示了这一点,但并没有直接解释:

返回字符串对象的规范表示。类String私有地维护着一个最初为空的字符串池。

当调用intern方法时,如果该池已经包含一个等于此String对象的字符串(根据equals(Object)方法确定),则返回池中的字符串。否则,将该String对象添加到池中,并返回对该String对象的引用。

由此可以得出结论:对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern() == t.intern()才为true。

所有文字字符串和值为字符串的常量表达式都被驻留。字符串文字在The Java™ Language Specification第3.10.5节中定义。

值得注意的是,Javadoc表示保证返回池中的字符串,但并未说明池本身是线程安全的(所以写作方式似乎为竞争线程可能会替换池条目敞开了大门,尽管我认为这种解释不太可能)。

JDK源码显示intern()是一个本地方法,无法揭示其线程安全性:
public native String intern();

我的关注点特别与以下内容有关,即在并发请求的情况下是否完全线程安全,保证每个唯一字符串值只创建一个MyObject(不仅仅是存储在缓存中):

public void myMethod(String myString)
{
    // Get object from cache, the cache itself is thread safe
    MyObject object = myCache.get(myString);
    if (object == null)
    {
        synchronized(myString.intern())
        {
            // Retry cache to avoid race condition
            object = myCache.get(myString);
            if (object == null)
            {
                object = new MyObject(myString);
                // ... do some startup / config of the object ...
                myCache.put(object);
            }
        }
    }
    // do something useful with the object
}

我希望避免在方法或缓存本身上同步,因为对象的创建可能需要一些时间(需要网络访问)。有一些解决方法,例如维护一个本地线程安全的字符串缓存/池,但除非必要,否则不值得这样做。在这种特定用例中,String.intern() 的内存影响(无法从缓存中删除 interned 字符串)与此无关(仅使用少量字符串)。
我相信 String.intern() 是线程安全的,上面的代码也没问题,但是缺乏来自可靠来源的直接确认让我稍感担忧。
这个问题之前已经在这里多次提出过,但没有提供具体的答案和参考资料:

2
为什么要使用String.intern()?为什么要在意同步String呢? - fge
此外,如果您正在寻找一个线程安全、并发友好的缓存实现,请使用Guava的LoadingCache。 - fge
@fge 为确保只构建和缓存特定字符串的一个对象。想象一下这些字符串是指向资源路径的,只能创建一个对象。此处的缓存仅是一个同步或并发的HashMap。 - Trevor Freeman
1
这实际上听起来像是你可能想要一个Guava Striped<Lock>,它以哈希方式将对象映射到锁。 - Louis Wasserman
1
它可能会为您节省一些复杂性,但笨拙的代价似乎很高。特别是,它使您的代码与任何尝试在字符串上同步的其他代码以“有趣”且难以调试的方式不兼容。也许他们正在使用常量字符串“foo”作为全局锁定,而您恰好获得了名为“foo”的资源路径,然后您就会遇到麻烦。这看起来可能会为您节省复杂性,但我仍然认为这是一个脆弱的黑客。 - Louis Wasserman
显示剩余4条评论
1个回答

4

听起来你可能需要使用Guava的Striped<Lock>,它以哈希方式将对象映射到锁上。在interned字符串上同步似乎是一个潜在的危险hack,如果你正在使用的其他代码也有相同的想法,可能会产生奇怪的副作用


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接