我有一个 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/