在 2 个线程中使用列表会导致 java.util.ConcurrentModificationException

working with list in 2 threads causes java.util.ConcurrentModificationException

获得了主线程和 myThread 的代码。当在主线程中按下某个按钮时 myThread.start() 被调用。 onCameraFrame 不断获取帧的颜色值并将其保存到 ArrayList<Double> rV。在 myThread 中,我需要 sout(rV),用它做一些事情并每 6 秒清理一次 rV。

我使用迭代器来做这样的事情,但我仍然在 myThread 的 sout 行得到 java.util.ConcurrentModificationException。请注意,它是随机发生的。例如,按下按钮后,sout 可能会正常工作 1 秒或 5 分钟,然后 - 异常。

我的建议是 rV 由 myThread 和 Main Thread (onCameraFrame) 同时使用。所以崩溃了。

需要建议。为此苦苦挣扎了几个小时。

这是代码。

public class Camera extends Activity implements CvCameraViewListener2 {

@Override
public void onCreate(Bundle savedInstanceState) {
    ..
    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ..
            myThread = new Thread(mRunnable);
            myThread.start();
        }
    ..
}

Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        Thread thisThread = Thread.currentThread();
        while (myThread==thisThread) {
            try {thisThread.sleep(6000);} 
            catch ..}
            mButton.post(new Runnable() {
                @Override
                public void run() {
                    /*logging*/
                    if (!rV.isEmpty()){
                        System.out.println("rV"+"("+rV.size()+")={"+rV.toString()+"}");
                    }

                    *//*clean data*//*
                    for (Iterator<Double> it = rV.iterator(); it.hasNext();) {
                        while(it.hasNext()){
                            Double t = it.next();
                            it.remove();
                        }
                    }
                }
            });
        }
    }
};

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    ..
    if (condition) {
        rV.add(somevalue);
    }
    return inputFrame.rgba();
}

您必须同步对数据结构的访问。一件好事是使用信号量(java.concurrent 包)。 信号量 s = 新信号量(1); -> 1 个许可证。

在您的线程中您获得了许可:s.aquire() - 如果没有许可,线程将在此处停止。完成工作后,您可以释放许可证:s.release()。 就是这样,现在只有一个线程可以访问数据

这里有两个潜在的问题。第一:ConcurrentModificationException 是在遍历 ArrayList 时从 ArrayList 中删除引起的。请参阅 this question 解决第一个问题。

第二个问题是 ArrayList 不是线程安全的。因此,您不能在迭代 'rV' 时修改它而不抛出异常。请改用 java.util.concurrent 包中的线程安全实现。例如,使用 ArrayBlockingQueue. See the tutorial on the Concurrency 获取更多详细信息

@DDsix 建议使用 CopyOnWriteArrayList. This may be appropriate in some situations, but inappropriate in others. The JavaDocs for CopyOnWriteArrayList 这样解释:

all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array... This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads.

您可以使用 CopyOnWriteArrayList,它基本上是一个线程安全的 ArrayList,或者使用 Collections.synchronizedList(new ArrayList<Object>()); 来创建您的列表(虽然这不是并发的,但是是同步的)