Ani*_*han 6 java multithreading hashmap
在尝试使用HashMap中的给定键获取值时,我们观察到了NullPointerException.
以下是我将用于说明问题的示例代码.
public class Test {
private Map<String, Integer> employeeNameToAgeMap = new HashMap<String, Integer>();
public int getAge(String employeeName) {
if (!employeeNameToAgeMap.containsKey(employeeName)) {
int age = getAgeFromSomeCustomAPI(employeeName);
employeeNameToAgeMap.put(employeeName, age);
}
return employeeNameToAgeMap.get(employeeName);
}
}
Run Code Online (Sandbox Code Playgroud)
在方法的最后一行获取NullPointerException,即"return employeeNameToAgeMap.get(employeeName);"
正如我们所看到的,employeeNameToAgeMap不是null,并且调用者也没有将employeeName作为null传递(这是我们已经接受了调用代码本身).
这个方法将从不同的线程以非常快的速度调用(来自一些计划运行的计时器任务,每100ms左右)
这个NullPointerException的原因似乎是为给定员工提供的值(年龄)为空,但事实并非如此,因为保证自定义API方法(getAgeFromSomeCustomAPI())返回给定员工的某个年龄,即使它返回null,那么异常stacktrace应该显示日志中的对应行而不是最后一行.
我唯一的假设是,当一个线程T1试图填充该缓存时,T2出现了,由于某种原因,它能够发现缓存已经拥有了employeeName,但是当它试图获得年龄时,它就抛出了一个NPE.但是我并不是100%确信当put()操作正在进行给定的键和值时,相同键的containsKey()返回true.
我知道需要增强此代码以满足同步问题(通过使用ConcurrentHashMap或锁),但期待知道此问题的真正原因.
我真的很感激帮助.
我相信您在致电期间遇到的是 HashMap 的重新哈希
return employeeNameToAgeMap.get(employeeName);
Run Code Online (Sandbox Code Playgroud)
人们可能会相信,如果 HashMap#containsKey(key) 返回 true,则应该保证调用 HashMap#get(key) 也应该返回一个有效值,只要该键没有从 HashMap 中删除。这可以通过以下事实来争论:HashMap#containsKey(key) 确实会检查键是否对应于有效值:
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
Run Code Online (Sandbox Code Playgroud)
但这是一个致命的误解。HashMap#containsKey(key) 只保证该键在调用之前的某个时间已经与某个值相关联。但它不保证,如果多个线程正在访问映射,HashMap#get(key) 也会返回相应的值。造成这种差异的原因是,使用任何键值对访问 HashMap#put(key,value) 的其他线程可能会强制重新哈希 HashMap,从而导致重新创建内部哈希表。如果在调用 HashMap#get(key) 期间发生这样的重新哈希,则 HashMap#get(key) 可能返回 null,即使您的 HashMap 之前在调用 HashMap#containsKey(key) 时返回 true。
如果你只是想避免 NullPointerException,你可以这样做:
public class Test {
private Map<String, Integer> employeeNameToAgeMap = new HashMap<String, Integer>();
public int getAge(String employeeName) {
final Integer age = employeeNameToAgeMap.get(employeeName);
if (age == null) {
age = getAgeFromSomeCustomAPI(employeeName);
employeeNameToAgeMap.put(employeeName, age);
}
return (int)age;
}
}
Run Code Online (Sandbox Code Playgroud)
这当然不会使您的代码线程保存,但您将不再遇到现在遇到的 NullPointerException 。
| 归档时间: |
|
| 查看次数: |
5670 次 |
| 最近记录: |