当前位置: 首页 > 面试题库 >

锁映射是并发操作的安全方法吗

姜弘新
2023-03-14
问题内容

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

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)提到的程序外,您的程序还有另一个错误。

设置locknullifuserId以前未见过,因为`putIfAbsent(…)不会返回新值,它会返回以前的值:

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

改为这样做:

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

computeIfAbsent(…)返回新值。另外,它的附带好处是,除非实际需要一个新的Lock对象,否则不创建该对象。(对@bowmore表示感谢)。

这个程序线程安全吗?

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

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

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

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



 类似资料:
  • 根据Go博客, 地图对于并发使用是不安全的:它没有定义当您同时读写地图时会发生什么。如果需要从并发执行的goroutines读取映射和向映射写入映射,则访问必须通过某种同步机制进行调解。(来源:https://blog.golang.org/go-maps-in-action) 有人能详细说明一下吗?跨例程的并发读取操作似乎是允许的,但是如果尝试读取和写入同一个键,并发读取/写入操作可能会生成竞争

  • 我在Java有一个很大map=concurrenthashMap(),而Key、Value是某种对象结构。假设这个映射的键集是keyset。 现在我下面有一个计算程序。我的问题是我如何能够获得一个更好的性能通过不使用全地图锁。是否有任何选择,如使用每键锁或使用其他类型的数据结构? 考虑到这是一个很大的映射,使用every-key锁可能不是一种可接受的方法。

  • 问题内容: 我的应用程序中有多个线程同时访问BitSet。该文档说: 如果没有外部同步,则BitSet对于多线程使用是不安全的。 它没有说读或写是否不安全。谁能解释。 问题答案: 仅当初始化的最后一个操作与读取该操作的操作之间存在“先于”关系时,A 对于只读操作才是安全的。 最简单的方法是使用。例如: 这足以确保“安全发布”。 但是,如果您不执行此类操作,则无法保证读取的线程将看到完全初始化的状态

  • 在我的服务器中。xml我有以下配置: 认证后我的主题是这样的: 组 ID 是由我的自定义登录模块设置的,因为 ldap 注册表中的组在具有成员的属性中没有条目,因此不是由标准 Liberty 登录模块设置的。因此,在我的自定义模块中,我在LDAP中搜索用户,并将条目的组添加到私有凭据的groupIds数组中: 我还需要做些什么吗?或者只需要将组的字符串添加到数组中就足够了? 我还尝试使用secur

  • 问题内容: 我需要一个线程安全映射,我有类似这样的内容:(我对Java很陌生) 问题答案:

  • 我正在尝试使用流将一个列表映射到另一个列表。 原始列表的某些元素无法映射。也就是说,映射函数可能无法找到合适的新值。 null 对更好的方法的建议?或者我应该把溪流全部挖开,用好的旧循环?