首页 > 解决方案 > 致命异常:主进程 PID:20793 java.util.ConcurrentModificationException

问题描述

该程序是为相应地扫描标签的扫描仪/终端编写的。
发生错误的方法:

public synchronized void generateBlocksWithAfterRFIDRead(Object obj) {
            if (searchRFID != null && !isSync) {
                HashSet<String> newmarks = (HashSet<String>) obj;
                if (newmarks.isEmpty()) return;
                int i = 0;
    
                Log.w("Тэг", "В новом наборе всего тегов : " + newmarks.size());
                int w = 0;
                for (String mark : newmarks) {
                    Log.w("Тэг", "В новом наборе тэг номер " + i++ + " : " + mark);
                    if (!(marks == null)) {
                        if (marks.contains(mark)) {
                            Log.w("Tag", "Уже содержит такой тэг! Сам тэг : " + mark);
                            continue;
                        }
                    } else {
                        marks = new HashSet<>();
                    }
                    marks.add(mark);

线for (String mark: newmarks) {

据我了解,更改集合时会发生错误,但是如果您一次扫描“大量”标签,一次扫描就会触发此类错误 - 不会发生此类错误。
如何纠正这个错误?如何正确修改集合?

** UPD:** 使用上述方法的方法

public HandlerRFID(RFIDReader reader, SearchResultsActivity activity) {
            timeMillis1 = System.currentTimeMillis();
            myReader = RFIDReaderSingleton.reader;
            acitivityUISearchRes = activity;
            readedMarks = new HashSet<>();
    
    
            handler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    // Gets the image task from the incoming Message object.
                    // running on mainUi
                    if (msg.what == StatusesMainUIUpdate.ALL_MARKS_ARE_READED) {
                        acitivityUISearchRes.generateBlocksWithAfterRFIDRead(msg.obj);
                        readedMarks = null;
                    } else {
                        super.handleMessage(msg);
                    }
                }
            };
        }

堆栈跟踪:

/com.example.testingodata E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.testingodata, PID: 20793
        java.util.ConcurrentModificationException
            at java.util.HashMap$HashIterator.nextEntry(HashMap.java:851)
            at java.util.HashMap$KeyIterator.next(HashMap.java:885)
            at com.example.testingodata.View.SearchResultsActivity.generateBlocksWithAfterRFIDRead(SearchResultsActivity.java:401)
            at com.example.testingodata.HandlerRFID$6.handleMessage(HandlerRFID.java:184)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:154)
            at android.app.ActivityThread.main(ActivityThread.java:6123)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

标签: javaandroidmultithreadingexception

解决方案


HashSet文档

此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间修改集合,除了通过迭代器自己的删除方法之外,迭代器会抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

基本上,当您迭代一个集合时,您不应该修改同一个集合 - 您会在 等下看到同样的警告HashMapLinkedList它会导致不可预测的行为,并且您可以做出不同的更改来影响当前状态以不同的方式,所以他们只是让它失败并说“所以不要那样做!”

至于为什么会发生大批量而不是单次扫描:

请注意,不能保证迭代器的快速失败行为,因为一般来说,在存在不同步的并发修改的情况下,不可能做出任何硬保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。

因此,很可能更可能会注意到大量更改。它继续说“不要依赖这种行为”,所以你可能应该以不同的方式做事!通常,您只需创建一个单独Set的(一个空的,或您的原件的副本)并对其进行更改,然后在完成后将其退回或换掉原件。


为了清楚起见,这里的问题是您要在集合上创建一个迭代器(使用for循环),然后在迭代器在其上运行时修改该集合。我注意到你有synchronized你的方法,所以如果你也在不同的线程上访问同一个集合,你必须确保你也在以一种线程安全的方式来做 -HashSet链接谈到了一些关于这些东西


推荐阅读