我应该使用 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.
我们有服务器 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.