Android:Hashmap并发修改异常

时间:2015-09-18 09:34:59

标签: java android multithreading hashmap concurrentmodification

我的代码上不断出现并发修改异常。我只是迭代一个hashmap并修改值。从研究这个我发现人们说使用迭代器和iterator.remove等我尝试用这个实现并仍然得到错误。我想也许有多个线程可以访问它? (虽然在我的代码中,这个块只在一个线程中运行)所以我把它放在一个synchronized块中。但是,我仍然得到错误.....

 Map map= Collections.synchronizedMap(questionNumberAnswerCache);
        synchronized (map) {
            for (Iterator<Map.Entry<String, Integer>> it = questionNumberAnswerCache.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry<String, Integer> entry = it.next();
                if (entry.getKey() == null || entry.getValue() == null) {
                    continue;
                } else {
                    try {
                        Question me = Question.getQuery().get(entry.getKey());
                        int i = Activity.getQuery()
                                .whereGreaterThan(Constants.kQollegeActivityCreatedAtKey, lastUpdated.get("AnswerNumberCache " + entry.getKey()))
                                .whereEqualTo(Constants.kQollegeActivityTypeKey, Constants.kQollegeActivityTypeAnswer)
                                .whereEqualTo(Constants.kQollegeActivityQuestionKey, me)
                                .find().size();

                        lastUpdated.put("AnswerNumberCache " + entry.getKey(), Calendar.getInstance().getTime());

                        int old_num = entry.getValue();
                        entry.setValue(i + old_num);

                    } catch (ParseException e) {
                        entry.setValue(0);
                    }
                }

            }

        }

错误:

  java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:787)
        at java.util.HashMap$EntryIterator.next(HashMap.java:824)
        at java.util.HashMap$EntryIterator.next(HashMap.java:822)
        at com.juryroom.qollege_android_v1.QollegeCache.refreshQuestionAnswerNumberCache(QollegeCache.java:379)
        at com.juryroom.qollege_android_v1.QollegeCache.refreshQuestionCaches(QollegeCache.java:267)
        at com.juryroom.qollege_android_v1.UpdateCacheService.onHandleIntent(UpdateCacheService.java:28)
        at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.os.HandlerThread.run(HandlerThread.java:61)

3 个答案:

答案 0 :(得分:2)

发生了什么:

迭代器循环遍历地图。地图并不像列表,因为它并不关心订单。因此,当您向地图添加内容时,它可能会插入到中间,在您已经循环的对象中间的某个位置,最后等等。因此,它不会给您随机行为,而是失败。

您的解决方案:

同步映射和同步块允许您同时使用两个线程。它在这里并没有真正帮助,因为问题是同一个线程正以非法的方式修改它。

你应该做什么:

您可以保存要修改的密钥。制作带有键和新值的地图不会成为问题,除非这是一个非常重要的时间代码。

然后,您只需遍历newValues映射并更新oldValues映射。由于您没有迭代正在更新的地图,因此不存在问题。

或者您可以简单地遍历键(对于String s:yourMap),然后查找要更改的值。由于您只是遍历键,因此您可以自由更改值(但您无法删除值)。

您也可以尝试使用ConcurrentHashMap,它允许您修改它,但行为是未定义的,因此这是有风险的。只是改变值不应该导致问题,但是如果你添加或删除你永远不知道它是否会最终被迭代通过。

答案 1 :(得分:1)

创建一个物体,并锁定它 - 一个用脚射击自己的好方法。

我建议使用以下代码删除哈希映射。

HashMap<Key, Object> hashMap = new HashMap<>();
LinkedList<Key> listToRemove = new LinkedList<>();
for(Map.Entry<Key, Object> s : hashMap.entrySet()) {
    if(s.getValue().equals("ToDelete")){
        listToRemove.add(s.getKey());
    }
}
for(Key s : listToRemove) {
    hashMap.remove(s);
}

这不是最美丽,最快捷的选择,但它可以帮助您了解如何使用HashMap。

您将了解如何使用我的选项。您可以了解how to work iteratorshow to work iterators in loop。 (而不是简单地复制粘贴)

Iterator it = tokenMap.keySet()) 
while(it.hasNext()) {
    if(/* some condition */) it.remove();
}

答案 2 :(得分:1)

我建议您使用以下内容:

for(Key key : hashMap.keySet()) {
    Object value = hashMap.get(key);
    if(<condition>){
        hashMap.put(key, <new value>); 
}

如果您没有删除任何条目而只是更改值,这应该适合您。