我有一个奇怪的案例,只发生在生产环境中。
基本上,我们保留每个用户最近的选项的历史记录,然后像这样检索它们:
LinkedList l_recent = ApplicationEnvironment.getUserRecentOptions(username);
for (int i = 0; i < l_recent.size(); i++) {
l_recent.get(i); // When i == 2 throws a NPE
}
但是堆栈跟踪为空,并且get方法的javadoc仅描述了一个可能的异常:IndexOutOfBoundsException
如果index < 0 || index >= size
幸运的是,这仅发生在很少的用户上,我们通过清除最近选项的历史记录来修复它。
但是我仍然很想知道为什么会抛出NPE。
我唯一的猜测是,由于此列表是针对每个用户的,因此同一用户登录的次数可能不止一次,然后我们可能同时调用LinkedList的add或remove方法
这里是完整的代码:
import java.util.*;
public class App {
static class Option {
private String name;
Option(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static final HashMap<String, LinkedList<Option>> userOptions = new HashMap<>();
public static LinkedList<Option> getUserRecentOptions(String user) {
LinkedList<Option> options;
if (userOptions.containsKey(user)) {
options = userOptions.get(user);
} else {
options = new LinkedList<>();
userOptions.put(user, options);
}
return options;
}
public static void addRecentOptionToUser(String user, Option option){
LinkedList<Option> options = getUserRecentOptions(user);
for (int i = 0; i < options.size(); i++) {
Option opt = options.get(i);
if (opt.getName().equalsIgnoreCase(option.getName())) {
options.remove(i);
break;
}
}
options.addFirst(option);
// Max 4
if (options.size() > 5) {
options.removeLast();
}
}
public static void main() {
LinkedList<Option> recentOptions = getUserRecentOptions("demo");
for (int i = 0; i < recentOptions.size(); i++) {
recentOptions.get(i); // Throws NPE when i == 2 (sometimes...)
}
}
}
所以不是 get(i)
为空。如果执行for (Object lri : l_recent) {
,则ConcurrentModificationException可能指向并发问题。但是,LinkedList在用户的最近选项中保持共享状态,并且可能经常更新,因此LinkedList级别太低,无法共享。
制作提供高级访问权限的API:getLatest,getAndRemember,getListCopyOfAll。
存在NPE的原因可能是序列化/反序列化或get的某些内部,或分配了新的List。没有堆栈跟踪似乎指向上下文切换之类的东西。但是,这一切都不基于我这一方面的深刻知识。
在for循环中尝试l_recent.size()-1方法返回的大小和列表从0点开始
[好吧,这的确是一个多线程问题,如果我从不同的线程多次调用addRecentOptionToUser
,则会重现该问题,因为节点为空,将引发NPE。
解决方案显然是使此代码具有线程安全性。
谢谢