我只会翻译英文文本,请提供需要翻译的英文内容。

huangapple go评论74阅读模式
英文:

FATAL EXCEPTION: main Process 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) { 中,

据我理解,当更改集合时会发生错误,但是只有在一次扫描中扫描“大量”标签时才会触发此类错误,单次扫描不会出现此类错误。
如何纠正这个错误?如何正确修改集合?

更新: 使用上述描述的方法的方法

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)
英文:

The program is written for scanners / terminals that scan tags accordingly.
The method in which the error occurs:

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

The line for (String mark: newmarks) {

As I understand it, an error occurs when changing a collection, but such an error is triggered if you scan a "Large" number of tags at once, with a single scan - such errors do not occur.
How can this error be corrected? How can a set be modified correctly?

** UPD: ** method that uses the method described above

public HandlerRFID(RFIDReader reader, SearchResultsActivity activity) {
            timeMillis1 = System.currentTimeMillis();
            myReader = RFIDReaderSingleton.reader;
            acitivityUISearchRes = activity;
            readedMarks = new HashSet&lt;&gt;();
    
    
            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);
                    }
                }
            };
        }

Stacktrace:

/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)

答案1

得分: 4

来自HashSet文档

> 该类的iterator方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间修改了集合,除非通过迭代器自己的remove方法以外的任何方式,迭代器都会抛出ConcurrentModificationException。因此,面对并发修改,迭代器会迅速而干净地失败,而不是在将来的某个不确定的时间冒险地产生任意的、不确定的行为。

基本上,在遍历集合时,不应该修改同一个集合 - 你会在HashMapLinkedList等下面看到相同的警告。这会引起不可预测的行为,而且你可能会进行不同的更改,从而以不同的方式影响当前状态,所以它们只是让它失败并说“所以不要那样做!”

至于为什么在大批处理中会出现这种情况,而不是单个扫描:
> 请注意,由于在存在不同步的并发修改的情况下,迭代器的快速失败行为无法得到保证,因此通常情况下不可能在这种情况下做出任何硬性保证。快速失败迭代器会尽力抛出ConcurrentModificationException。

所以很可能只是更有可能注意到大量的更改。它继续说“不要依赖于这种行为”,所以你应该以不同的方式处理事情!通常你只需要创建一个单独的Set(一个空的,或者是原始集合的副本),并对其进行更改,然后在完成时返回它,或者将原始集合替换为它。


为了明确起见,问题在于你正在在集合上创建一个迭代器(通过for循环),并且在迭代器运行时修改了该集合。我注意到你的方法上有synchronized,所以如果你也在不同的线程上访问同一个集合,你必须确保以线程安全的方式进行操作 - HashSet链接中讨论了一些相关内容。

英文:

From the HashSet docs:

> The iterators returned by this class's iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator's own remove method, the Iterator throws a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

Basically, when you're iterating over a collection, you shouldn't modify that same collection - you'll see this same warning under HashMap, LinkedList etc. It causes unpredictable behaviour, and there are different changes you could make that would affect the current state in different ways, so they just make it fail and say "so don't do that!"

As for why it happens with large batches and not a single scan:
> Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis.

So it's probably just more likely that a large set of changes will get noticed. It goes on to say "don't rely on this behaviour" so you should probably do things in a different way! Usually you would just create a separate Set (an empty one, or a copy of your original) and make changes to that, and then return it or swap the original out for it when you're done.


Just to be clear, the problem here is that you're creating an iterator on a collection (with the for loop) and then modifying that collection while the iterator is running on it. I noticed you have synchronized on your method, so if you're also accessing the same collection on different threads, you have to make sure you're doing that in a thread-safe way too - the HashSet link talks a bit about that stuff

huangapple
  • 本文由 发表于 2020年9月18日 03:09:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63944760.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定