繁体   English   中英

在“this”与特定资源上同步

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM