锁映射是并发操作的安全方法吗 [英] Is Map of Locks a safe approach for concurrent operations

查看:66
本文介绍了锁映射是并发操作的安全方法吗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

要求仅允许一个线程执行用户管理(创建/更新/导入)操作,但不允许多个线程同时为同一用户执行用户操作.例如,当线程A正在创建用户A时,则不允许线程B同时允许用户B导入或创建用户A,但是允许线程B导入用户B.下面的代码线程对于这些要求是否安全?

The requirement is only single thread must be allowed to perform user management (create/update/import) operations but multiple threads are not allowed to perform user operations simultaneously for the same user. For example, When Thread A is creating User A then concurrently Thread B must not be allowed to import User A Or Creating User A but Thread B is allowed to import User B. Is the below code thread safe for these requirements?

public class UserManagement {

    ConcurrentHashMap<Integer, Lock> userLock = new ConcurrentHashMap<>();

    public void createUser(User user, Integer userId) {
        Lock lock = userLock.putIfAbsent(userId, new ReentrantLock());
        try {
            lock.lock();
            //create user logic
        } finally {
            lock.unlock();
        }
    }

    public void importUser(User user, Integer userId) {
        Lock lock = userLock.putIfAbsent(userId, new ReentrantLock());
        try {
            lock.lock();
            //import user logic
        } finally {
            lock.unlock();
        }
    }

    public void updateUser(User user, Integer userId) {
        Lock lock = userLock.putIfAbsent(userId, new ReentrantLock());
        try {
            lock.lock();
            // update user logic
        } finally {
            lock.unlock();
        }
    }
}

推荐答案

您的程序除了安德鲁·利金(Andrew Lygin)提到的那个错误之外,还有另一个错误.

Your program has another bug besides the one that Andrew Lygin mentioned.

如果以前没有看到userId,则将lock设置为null,因为`putIfAbsent(...)不会返回新值,因此它会返回上一个值:

This sets lock to null if userId has not previously been seen, because `putIfAbsent(...) does not return the new value, it returns the previous value:

Lock lock = userLock.putIfAbsent(userId, new ReentrantLock());

相反,请执行以下操作:

Do this instead:

Lock lock = userLock.computeIfAbsent(userId, k -> new ReentrantLock());

computeIfAbsent(...)返回 new 值.另外,它的附带好处是,除非实际需要一个新的Lock对象,否则不创建一个新的Lock对象. (对@bowmore的建议表示敬意.)

computeIfAbsent(...) returns the new value. Also, it has the side benefit of not actually creating a new Lock object unless one actually is needed. (Kudos to @bowmore for suggesting it.)

程序线程安全吗?

假设您已修复错误,我们仍然无法告知程序.我们所能知道的是,您的UserManagement类的实例将不允许对同一userId的这三个方法中的任何一个进行重复调用.

Assuming you fix the bugs, We still can't tell about the program. All we can tell is that an instance of your UserManagement class will not allow overlapped calls to any of those three methods for the same userId.

是否使程序线程安全取决于您的使用方式.例如,您的代码不允许两个线程同时更新同一userId,但是如果它们 try ,则将允许它们一个接一个地更新.您的代码将无法控制哪个代码优先-操作系统会做到这一点.

Whether or not that makes your program thread safe depends on how you use it. For example, your code won't allow two threads to update the same userId at the same time, but if they try, it will allow them to go one after the other. Your code won't be able to control which one goes first---the operating system does that.

您的锁定可能会阻止两个线程将用户记录保持为无效状态,但是它们会将其保持为 right 状态吗?该问题的答案超出了您向我们展示的一门课的范围.

Your locking likely will prevent the two threads from leaving the user record in an invalid state, but will they leave it in the right state? The answer to that question goes beyond the bounds of the one class that you showed us.

线程安全性不是 composeable 属性.也就是说,完全使用线程安全类构建某些对象并不能保证整个对象都是线程安全的.

Thread safety is not a composeable property. That is to say, building something entirely out of thread safe classes does not guarantee that the whole thing will be thread-safe.

这篇关于锁映射是并发操作的安全方法吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆