错误看起来像这样
Exception in thread "Thread-1" java.lang.NullPointerException
at java.util.LinkedHashMap$Entry.remove(LinkedHashMap.java:332)
at java.util.LinkedHashMap$Entry.recordAccess(LinkedHashMap.java:356)
at java.util.LinkedHashMap.get(LinkedHashMap.java:304)
at Server.getLastFinishedCommands(Server.java:9086)
at Server.processPacket(Server.java:484)
at PacketWorker.run(PacketWorker.java:34)
at java.lang.Thread.run(Thread.java:744)
在getLastFinishedCommands
内,我使用
public List<CCommand> getLastFinishedCommands(UserProfile player) {
List<CCommand> returnList = new ArrayList<CCommand>();
if(!finishedCommands.containsKey(player.myWebsitecmd-1)) {
getSavedState(player);
return null;
}
try { //<-- added this try/catch so it doesn't happen again.
//Get commands.
CCommand cmd;
long i;
long startIndex = player.myWebsitecmd;
long endIndex = startIndex+LIMIT_COMMANDS;
for(i = startIndex; i <= endIndex; i++) {
cmd = finishedCommands.get(i); //<-- this is line 9086
if(cmd == null) {
return returnList;
}
returnList.add(cmd);
}
} catch(Exception e) {} //<-- added this try/catch so it doesn't happen again.
return returnList;
}
我想创建一个自动删除旧条目的地图,因此我使用了这个代码段
public static <K, V> Map<K, V> createLRUMap(final int maxEntries) {
return new LinkedHashMap<K, V>(maxEntries*3/2, 0.7f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
};
}
像这样使用它
public static int final MAX_COMMANDS_QUEUE = 5000;
public Map<Long, CCommand> finishedCommands = createLRUMap(MAX_COMMANDS_QUEUE);
显然,当使用多个线程时会出现某种CocurrentModifcationException ..但为什么它会在内部崩溃,任何人都知道如何像CocurrentHashMap一样使用它?我试图解决这个问题,而不是仅仅尝试在整个getLastFinishedCommands
函数中使用try / catch。
我想要一张从旧垃圾中清除自己的地图,但仍然保留至少5000个键/值条目。
答案 0 :(得分:4)
基于堆栈跟踪,我假设代码尝试从其项已被另一个线程删除的索引中删除该值。这使得它在访问NPE
引用的属性时抛出null
。也许你应该尝试同步集合
来自LinkedHashMap
请注意,此实现未同步。如果多个线程同时访问链接的哈希映射,并且至少有一个线程在结构上修改了映射,则必须在外部进行同步。这通常通过在自然封装地图的某个对象上进行同步来实现。如果不存在这样的对象,那么地图应该被&#34;包裹&#34;使用Collections.synchronizedMap方法。这最好在创建时完成,以防止意外地不同步访问地图:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
答案 1 :(得分:1)
你说多个线程正在访问这个地图。这确实可能导致remove
实例的LinkedHashMap.Entry
操作中的NPE。这是此方法的实现:
private void remove() {
before.after = after;
after.before = before;
}
此处before
和refer
之后是当前条目的链接前任和后继者。如果另一个线程已经改变了条目之间的链接,这当然会导致意外的行为,例如NPE。
解决方案是 - 您猜对了 - 将生成的地图包装在同步地图中。如:
public static <K, V> Map<K, V> createLRUMap(final int maxEntries) {
Map<K,V> result = new LinkedHashMap<K, V>(maxEntries*3/2, 0.7f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
};
return Collections.synchronizedMap(result);
}
这个同步包装器确实会同步所有对底层映射的调用,因此只允许一个线程通过每个方法(例如get,put,contains,size等)。