【问题标题】:working with list in 2 threads causes java.util.ConcurrentModificationException在 2 个线程中使用列表会导致 java.util.ConcurrentModificationException
【发布时间】:2016-06-11 00:00:31
【问题描述】:

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

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

我的建议是 myThread 和主线程 (onCameraFrame) 一次使用 rV。所以它崩溃了。

需要建议。挣扎了好几个小时。

这是代码。

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 android multithreading android-studio iterator


【解决方案1】:

这里有两个潜在的问题。首先:在迭代ArrayList 时从ArrayList 中删除引起的ConcurrentModificationException。请参阅this question 以解决第一个问题。

第二个问题是ArrayList 不是线程安全的。因此,您不能在迭代时修改 'rV' 而不会引发异常。请改用java.util.concurrent 包中的线程安全实现。例如,使用ArrayBlockingQueue。详情请见the tutorial on the Concurrency

@DDsix 建议使用CopyOnWriteArrayList。这在某些情况下可能是合适的,但在其他情况下是不合适的。 CopyOnWriteArrayList 的 JavaDocs 是这样解释的:

所有可变操作(添加、设置等)都是通过制作底层数组的新副本来实现的...这通常成本太高,但可能比替代方法更有效当遍历操作的数量远远超过突变时,并且当您不能或不想同步遍历但需要排除并发线程之间的干扰时很有用。

【讨论】:

  • 大部分线程安全集合都允许您进行脏读(无锁定读取),但删除和插入将需要锁定。
  • ArrayBlockingQueue当然没有这个问题...你指的是哪个收藏?
  • @AustinD,我试过ArrayBlockingQueue(比如说,ABK)我遇到了另一个问题。我不能在主线程中声明 ABK,因为它的大小(帧颜色值的数量)直接取决于不断变化的相机 fps。因此,我尝试在 myThread 中创建 ABK 并遇到相同的问题,以便将 rV 克隆到 ABK 中,我必须在 myThread 中循环遍历 rV。这段代码我粘贴在 myThread 中。 ArrayBlockingQueue&lt;Double&gt; rVBQ = new ArrayBlockingQueue&lt;Double&gt;(rV.size()); for (Double d : rV) { rVBQ.add(d); }
  • 为什么不直接使用rV.clear(),然后删除for (Iterator&lt;Double&gt; it = rV.iterator(); it.hasNext();) {while(it.hasNext()){ Double t = it.next(); it.remove(); } }
  • 好吧,我不明白你为什么这么说 - 迭代器的使用很好,错误在sout(rV.size+rV.toString) 处弹出。无论如何,我注意到 ABQ 不必像 rV 那样 精确 大小,它是必须指定的 ABQ 的 max 容量。我将它设置为 1000,所以我根本没有使用 ArrayList,并且能够在 Main 和 myThread 中访问 ABQ,没有任何例外。感谢您的提示!
【解决方案2】:

你可以使用CopyOnWriteArrayList,它基本上是一个线程安全的ArrayList,或者使用Collections.synchronizedList(new ArrayList&lt;Object&gt;());来创建你的List(虽然这不会是并发的,但是是同步的)

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 2021-07-23
      • 2017-02-04
      • 2020-08-29
      • 2018-04-25
      相关资源
      最近更新 更多