【问题标题】:Will this method work thread-safe and without Deadlocks这种方法是否可以在线程安全且没有死锁的情况下工作
【发布时间】:2019-03-05 14:27:52
【问题描述】:
public int saveUserToMap(User user) {
    ReentrantLock lock;
    if(this.userLocks.containsKey(user.getId())) {
        lock = this.userLocks.get(user.getId());
    } else {
        lock = new ReentrantLock();
        ReentrantLock check = this.userLocks.putIfAbsent(user.getId(), lock);
        if(check != null)
            lock = check;
    }

    if(lock.isLocked())
        try {
            lock.wait();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    lock.lock();

    this.users.put(user.getId(), user);
    this.usersByName.put(user.getUsername(), user);
    this.usersByEmail.put(user.getEmail(), user);

    lock.unlock();
    lock.notify();

    return user.getId();
}

嘿,我只是想请 Java 开发人员检查我的代码是否是线程安全的并且没有死锁,因为我想在我的项目中使用它。 Users、UsersByName 和 UsersByEmail 是 ConcurrentHashMap,其中 String、Integer 为 key,User 对象为 Value。 UserLocks 是一个 ConcurrentHashMap,其中 Integer(显然用户 id 作为键)和 ReentrantLock 作为值。 我想同步三个HashMap。 如果有人有更好的解决方案来制作包含三个键的并发映射,那么最好将其发布在这里。性能也很重要。

【问题讨论】:

  • 我要同步三个HashMap有什么目的?
  • 这三个 hashmap 的 value 相同但 key 不同。我不希望这样,因为多线程三个哈希图具有不同的值。比如id、username、email的用户在三个hashmap中表示,现在两个线程要更新用户,我不希望三个hashmap有不同的值。
  • 我担心如果 2 个具有相同 ID、用户名或电子邮件但不是全部 3 个的 User 实体被添加到您的地图中会发生什么?其他地方是否考虑到了这一点?

标签: java concurrency thread-safety deadlock


【解决方案1】:

它是线程安全的。

如果 userId 已经在 map 中,代码获取锁并使用它进行同步。如果没有,ConcurrentHashMap 会提供同步以避免竞争条件以对同一 id 使用不同的锁。

之后,有一段无用的代码,你可以去掉:

if(lock.isLocked())
    try {
        lock.wait();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

不需要,因为正在使用lock.lock() 完成同步。不需要尝试使用wait()notify() 与锁定对象再次同步。(实际上,它没有按您的预期工作,多个线程可以在同一个锁定对象上调用lock.isLocked() 并得到一个假,直到任何的线程调用lock.lock(),但锁定和解锁之间的所有内容一次只能由一个线程执行)。

此外,通常的良好做法是在 finally 块中调用 lock.unlock()

【讨论】:

    【解决方案2】:

    我会使用synchronized 以简单的方式完成。

    class UserMaps {
        private Map<Integer, User> users = new ConcurrentHashMap<>();
        private Map<String, User> usersByName = new ConcurrentHashMap<>();
        private Map<String, User> usersByEmail = new ConcurrentHashMap<>();
    
        public synchronized int put(User user) {
            users.put(user.getId(), user);
            usersByName.put(user.getUsername(), user);
            usersByEmail.put(user.getEmail(), user);
            return user.getId();
        }
    }
    

    只要您的所有 getter 也是 synchronized,这将确保所有地图都得到一致更新。

    但是,如果您想要更好的性能并且想要避免将您的方法全部设为synchronized,那么请使用ReadWriteLock

    class UserMaps {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        private Map<Integer, User> users = new ConcurrentHashMap<>();
        private Map<String, User> usersByName = new ConcurrentHashMap<>();
        private Map<String, User> usersByEmail = new ConcurrentHashMap<>();
    
        public int saveUserToMap(User user) {
            lock.writeLock().lock();
            try {
                users.put(user.getId(), user);
                usersByName.put(user.getUsername(), user);
                usersByEmail.put(user.getEmail(), user);
                return user.getId();
            } finally {
                lock.writeLock().unlock();
            }
        }
    
        public User getById(int id) {
            lock.readLock().lock();
            try {
                return users.get(id);
            } finally {
                lock.readLock().unlock();
            }
        }
    }
    

    【讨论】:

    • 好的,我还有一个问题,将这些地图用于用户数据是否有用。我将用户数据保存在 mysql 数据库中,并在用户登录时检索它们。每次当用户进入另一个页面时,这些用户数据都会更新。我应该使用地图并在那里保存我的应用程序中的更改,同时在后台有一个数据库,还是应该直接从数据库下载它。我想让应用程序尽可能高性能。
    • 由于集合是 CHM,如果您只在其中一个中查找,则不需要读取锁,对吗?我怀疑锁的重点是保持 3 个集合同步。
    • 如果您正在同步(通过synchronizeLock)对地图的所有访问,那么使用HashMap 不是更有意义吗?
    • @Gray 是的,但是您可以连续读取两次并获得错误的名称/电子邮件对。 GDPR 泄漏的来源!
    • 我同意@OldCurmudgeon。我只是在 get* 方法中看不到 2 个操作,所以我不确定锁在做什么。就这样。没什么大不了的。干杯老兄。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    • 1970-01-01
    • 2023-02-23
    相关资源
    最近更新 更多