java - ConcurrentHashMap putIfAbsent 第一次

标签 java java.util.concurrent concurrenthashmap concurrent-programming

我有一个 ConcurrentHashMap 和一个将字符串放入映射中的方法,然后我根据插入的值在同步块(synchronized block)中执行一些操作。

putIfAbsent 返回与指定键关联的先前值,如果该键没有映射,则返回 null - 基于官方文档

根据 putIfAbsent 是否返回 null 来执行 2 个操作。

现在这就是窍门。我希望首先执行第一个操作(当 putIfAbsent 返回 null 时),并暂停所有其他线程。 我的代码在 95% 的情况下都能按预期工作。

private final ConcurrentHashMap<String, String> logins = new ConcurrentHashMap<>();

public void login(String id){
      String inserted=logins.putIfAbsent(id,id);

      synchronized(logins.get(id)){
           if(inserted==null){
                System.out.println("First login");
           }else{
                System.out.println("Second login");
           }           
      }
}

如果我从不同线程使用相同的字符串值调用此方法 login("some_id"); 有时(大约 5% 的时间)我会在控制台上收到此消息:

Second login
First login

我需要更改什么才能始终确保首先执行首次登录

更新:根据我的阅读,logins.get(id) 是否有可能返回 null ,从而在 null 对象上同步?

最佳答案

sometimes (around 5% of the time) I get this message on console:

您有一个竞争条件,即第一个添加的不是第一个打印的。

在这种情况下,您的主要瓶颈是使用 System.out,这是一个满足的资源,比使用 Map、并发或其他方式要昂贵很多倍。

在这种情况下,您也可以简化代码,这样您就只获得一个锁,即您无论如何都必须获得的 System.out 上的锁

// use System.out as lock so logging of actions is always in order.
private final Set<String> ids = Collections.newSetFromMap(new HashMap<>());

public void login(String id) {
    synchronized (System.out) {
        System.out.println(ids.add(id) ? "First login" : "Second login")l
    }
}

关于java - ConcurrentHashMap putIfAbsent 第一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38944916/

相关文章:

java - 在java中读取对象错误java.io.StreamCorruptedException : invalid type code: AC

java - ConcurrentHashMap 实现从 Java 7 更改为 8

java - 在 ConcurrentHashMap 中放置线程安全的同时增加当前值?

java - 如何从 Gmail 中获取联系人

java - nginx,为什么不能在if部分使用auth_request

java - Hadoop Hive 无法将源移动到目标

java - 如何从 ScheduledExecutorService 获取队列和池大小

java - 阻塞队列 "union"的实现

java - ConcurrentHashMap 上的同步

java - 跨线程和逻辑的同步