此方法是否可以在线程安全且没有死锁的情况下工作

时间:2019-03-05 14:27:52

标签: java concurrency thread-safety deadlock

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作为键,User对象作为Value。 UserLocks是一个带有Integer(显然是用户ID作为键)和ReentrantLock作为值的ConcurrentHashMap。 我想同步三个HashMap。 如果有人有更好的解决方案来制作带有三个键的并发地图,最好在此处发布。性能也很重要。

2 个答案:

答案 0 :(得分:1)

这是线程安全的。

如果userId已经在映射中,则代码获取锁并使用它进行同步。如果没有,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()

答案 1 :(得分:1)

我会使用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();
    }
}

只要您的所有获取者也都是synchronized,这将确保所有地图都一致更新。

但是,如果您希望获得更好的性能,并且希望避免将方法synchronized做成 all ,那么请使用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();
        }
    }
}