2

错误看起来像这样

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 个键/值条目的地图。

4

2 回答 2

5

基于堆栈跟踪,我假设代码尝试从索引中删除该值,该索引的项目已被另一个线程删除。这使得它NPE在访问null引用的属性时抛出。可能,您应该尝试同步集合

从文档LinkedHashMap

请注意,此实现不同步。如果多个线程同时访问链接的哈希映射,并且至少有一个线程在结构上修改映射,则必须在外部同步。这通常是通过同步一些自然封装地图的对象来完成的。如果不存在这样的对象,则应使用 Collections.synchronizedMap 方法“包装”地图。这最好在创建时完成,以防止对地图的意外不同步访问:

   Map m = Collections.synchronizedMap(new LinkedHashMap(...));
于 2014-04-01T07:16:00.057 回答
1

您说多个线程正在访问此映射。这确实会导致NPE在remove运行一个LinkedHashMap.Entry实例。这是此方法的实现:

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 等)只允许一个线程通过。

于 2014-04-01T07:27:00.040 回答