[英]synchronize on "this" vs specific resource
我想了解哪种方法正确/性能更好,使用同步方法还是在特定资源上同步? 我的理解是,在需要避免并发修改的特定 object 上获取锁定会更快,因为所有其他尝试修改其他对象的线程都可以自由进行。
下面的示例代码为此同步“createSession”方法
class Solution {
public String getSessionKey(int user) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
throw new InvalidSessionException(SESSION_NOT_FOUND);
}
public synchronized String createSession(int user) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
UserSession session = new UserSession(user, generateSessionId(), LocalDateTime.now(ZoneOffset.UTC));
someConcurrentHashMap.put(user, session);
sessionKeyToUser.put(session.getKey(), user);
return someConcurrentHashMap.get(user).getKey();
}
public int getUserBySessionKey(String sessionKey) {
return sessionKeyToUser.getOrDefault(sessionKey, -1);
}
}
下面的代码获取特定 object 上的锁定,如果它存在于其他地方。
class Solution {
private final Object createSessionLock = new Object();
public String getSessionKey(int user) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
throw new InvalidSessionException(SESSION_NOT_FOUND);
}
public String createSession(int user) {
// if user exists then synchronized on the user object
// else synchronized on lock object
if (someConcurrentHashMap.containsKey(user)) {
return createForExistingUser(user);
}
return createForNewUser(user);
}
public int getUserBySessionKey(String sessionKey) {
return sessionKeyToUser.getOrDefault(sessionKey, -1);
}
private String createForExistingUser(int user) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
UserSession userSession = someConcurrentHashMap.get(user);
synchronized (userSession) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
UserSession session = new UserSession(user, generateSessionId(), LocalDateTime.now(ZoneOffset.UTC));
someConcurrentHashMap.put(user, session);
sessionKeyToUser.put(session.getKey(), user);
return someConcurrentHashMap.get(user).getKey();
}
}
private String createForNewUser(int user) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
synchronized (createSessionLock) {
if (validateSessionKey(user)) {
return someConcurrentHashMap.get(user).getKey();
}
UserSession session = new UserSession(user, generateSessionId(), LocalDateTime.now(ZoneOffset.UTC));
someConcurrentHashMap.put(user, session);
sessionKeyToUser.put(session.getKey(), user);
return someConcurrentHashMap.get(user).getKey();
}
}
}
我的理解是,在需要避免并发修改的特定 object 上获取锁定会更快,因为所有其他尝试修改其他对象的线程都可以自由进行。
我认为您可能误解了细粒度锁定的概念。 假设您有一个程序,其中大量数据由多个线程共享。 还假设不同的线程通常只想访问该数据的一小部分,并且这些部分通常不重叠。
您面临一个选择:仅使用一个锁来保护整个数据库既简单又万无一失。 但是拥有许多锁来保护其中的一小部分会更有效。 第一种方式是粗粒度锁定的最极端版本,第二种方式是“细粒度”。 细粒度的锁定可能更有效,但这意味着程序员需要做更多的工作:编写更多代码,测试更多代码。 而且,如果不同的线程有时确实需要访问数据库的重叠部分,那么细粒度锁定会带来死锁的风险。
我认为您可能不理解的部分是您选择将哪些对象用作锁的重要性。
在 Java 中,您选择使用哪个 object 作为锁没有意义。 在 Java 中,当一个线程进入synchronized (foobar)
块时,它唯一阻止的是,它阻止其他线程同时在同一个 object, foobar
上同步。 特别是,它不会阻止其他线程访问foobar。
在某些 object 上同步完全独立于以任何其他方式使用相同的 object。
反对使用所谓的“同步方法”的真正理由是,当你这样做时,你正在同步的 object 很可能是一个公开可见的 object。
如果您的class Solution
及其public synchronized String createSession(...)
方法是库的一部分,那么您的库的某些客户端可能会使用Solution
实例作为锁。 他们这样做不是一个聪明的主意,但他们被允许这样做。 而且,如果他们这样做,那么他们的代码将使用与您的代码相同的 object 进行锁定。 这可能会导致他们的代码和你的代码之间发生奇怪的交互。
更糟糕的是,是。 如果您稍后在您的代码在Solution
实例上同步时进行了更改,那么当他们升级到您的库的最新版本时,可能会破坏他们的代码。
相反,如果您只在private final Object lock=new Object()
对象上进行同步,那么您可以避免这个问题,并且可能会在您的库客户端中强化您的库“健壮”和“可靠”的错觉。
更好的方法是 go 与特定资源同步。 如果我们要在方法级别进行同步,那么很多线程将在等待 state 并且可能会出现这样的情况,即方法中的很多东西可以被多个线程访问,只留下需要的重要代码块没有意义的线程安全。 大多数时候我们可以 go 与资源级别同步以有效利用可用资源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.