我应该使用 Java 字符串池根据唯一客户 ID 进行同步吗?

Should I use Java String Pool for synchronization based on unique customer id?

我们有服务器 API 来支持一千万台设备上的客户端 运行。通常客户每天调用服务器一次。那是每秒看到大约 116 个客户端。对于每个客户端(每个客户端都有唯一的 ID),它可能会同时进行多个 APIs 调用。服务器然后需要对来自同一客户端的那些 API 调用进行排序。因为,那些 API 调用将更新 Mongodb 数据库中的相同文档。例如:上次上线时间和其他嵌入文档。

因此,我需要创建一个基于客户端唯一Id的同步机制。经过一些研究,我发现 String Pool 很有吸引力并且易于实现。但是,有人评论说锁定字符串池可能与其他也使用它的 library/module 冲突。因此,字符串池永远不应该用于同步目的。这个说法是真的吗?或者我应该像下面 link 中提到的那样通过 WeakHashMap 实现我自己的 "String Pool" 吗?

Java 中对字符串池实现的很好解释: http://java-performance.info/string-intern-in-java-6-7-8/

文章指出不应将字符串池用于同步: http://www.journaldev.com/1061/thread-safety-in-java

==================================

感谢BeeOnRope的建议,我将使用Guava的Interner来解释解决方案。这样,不会同时发送多个请求的客户端不会被阻塞。此外,它保证同一时间只处理来自一个客户端的一个 API 请求。顺便说一下,我们需要使用包装器 class,因为按照 BeeOnRope 和他在回答中提供的 link 所解释的那样锁定 String 对象是个坏主意。

public class Key {
    private String id;

    public Key(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ( (id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Key other = (Key)obj;
        if (id == null) {
            if (other.id != null) return false;
        } else if (!id.equals(other.id)) return false;
        return true;
    }
}

Interner<Key> myIdInterner = Interners.newWeakInterner();

public void processApi1(String clientUniqueId, RequestType1 request) {
    synchronized(myIdInterner.intern(new Key(clientUniqueId))) {
        // code to process request
    }
}

public void processApi2(String clientUniqueId, RequestType2 request) {
    synchronized(myIdInterner.intern(new Key(clientUniqueId))) {
        // code to process request
    }
}

好吧,如果您的字符串足够独特(例如,通过加密哈希生成1),那么在客户端 ID 上同步可能 有效 ,只要你先给他们打电话String.intern()。由于 ID 是唯一的,您不太可能 运行 与其他模块发生冲突,除非您碰巧将您的 ID 传递给它们 并且 它们遵循以下不良做法锁定他们。

也就是说,这可能是个坏主意。如果其他人锁定同一个 String 实例,那么一天 运行 陷入不必要的争用的可能性很小,主要问题是您必须 intern() 所有 String 对象,并且由于字符串实习生 table 的本机实现、它是固定大小等,这通常会遇到性能不佳的问题。如果您真的需要仅基于 String 进行锁定,那么您最好使用 Guava 的 Interners.newWeakInterner() interner implementation, which is likely to perform much better. Wrap your string in another class to avoid clashing on the built-in String lock. More details on that approach

除此之外,通常还有另外一种天然的对象需要锁定,例如会话对象中的锁定等

这与 非常相似,后者有更充实的答案。


1 ... 或者,至少,至少有足够的位来使冲突不太可能发生,并且如果您的客户端 ID 不属于您的 attack surface.