【问题标题】:ConcurrentModificationException TimerTaskConcurrentModificationException TimerTask
【发布时间】:2018-02-06 02:03:04
【问题描述】:

我在取消第一个计时器后启动第二个计时器时收到ConcurrentModificationException。两个计时器都使用单独的ArrayList 并对其进行迭代。没有对列表进行删除/修改,仍然抛出ConcurrentModificationException

当我停止第一个计时器并立即启动第二个计时器时,就会发生这种情况。如果我在启动第二个计时器之前等待几秒钟,那么它工作正常。

我最终在两个计时器上制作了传入列表的副本,将副本传递给计时器,但仍然出现错误。不知道为什么,因为我正在遍历列表并仅读取值。

计时器 #1 的代码:

private void timerwork(final List<Object> list) throws IOException {
    timer = new Timer();
    final List<Object> taskList = list;
    timer.scheduleAtFixedRate(new CustomTimerTask(taskList) {
        @Override
        public void run() {
            if (taskList != null && !taskList.isEmpty()) {
                synchronized (taskList) {
                    for (Object o: taskList) {
                        try {
                            // this method takes each object, 
                            // does some logic and writes to a flat file, 
                            // it does not modify the object itself, 
                            // but just reads it and does some calculation 
                            // on some local variables
                            valueIncrementOperation(o);
            } catch (IOException e) {
                timer.cancel();
                timer.purge();
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        //once exited out of the loop, it copies the flat file to another file
        try {
            copyFileUsingFileChannels(source, finaldest);
        } catch (IOException ignore) {
        }
    }
}

public synchronized void valueIncrementOperation(Object o) throws IOException {
    DataInputStream d = new DataInputStream(new FileInputStream(sourcefile));
    DataOutputStream out = new DataOutputStream(new FileOutputStream(tempfile));

    initialValue = Long.parseLong(o.getDefaullt_value());
    String count;
    String t;
    while ((count = d.readLine()) != null) {
        String u = count.toUpperCase();
        String[] z = u.split(" ");

        if (z[0].contains(o.getO())) {
            // .............. *snip* ..............
            out.writeBytes(t + "\n");
        }
    d.close();
    out.close();
    copyFileUsingFileChannels(source, initialDest);
}

CustomTimerTask 代码:

public class CustomTimerTask extends TimerTask {
    private List<Object> list = new ArrayList<Object>();

    public CustomTimerTask(List<Object> list) {
        this.list = list;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
    }
}

定时器 2 有类似的逻辑:即,它在传递给定时器 2 之前复制传入的列表。

我仍然收到此错误:

Exception in thread "Timer-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)

解决方法是在启动 timer2 之前等待几秒钟。有没有更好的方法来解决这种情况?

【问题讨论】:

  • 显而易见的问题:com.comcast.ams.simulator.service.OidTypesDao$2.run(OidTypesDao.java:307) 是什么? (显而易见的答案:您处于一个 foreach 循环中,您可以在其中从列表中删除项目。)
  • this.list = list; 不会制作列表的副本,它只是对 same 列表进行新的引用。
  • 一个简单的解决方案可能是将您的 List 替换为 java.util.concurrent.CopyOnWriteArrayList。这可能会解决您的问题。
  • @DavidC - 在调用定时器构造函数final List&lt;Object&gt; taskList = list; 并将taskList 传递给构造函数之前,我正在复制列表
  • 将其更改为final List&lt;Object&gt; taskList = new ArrayList&lt;Object&gt;(list); 以进行复制,或使用@Gennaro 提到的写时复制实现。

标签: java arraylist timer timertask concurrentmodification


【解决方案1】:

我们从堆栈跟踪中知道异常是从ArrayList 内部引发的。因此,让我们看一下处理Lists 的代码行。 (为了便于阅读,去掉了多余的代码)

首先是方法调用:

private void timerwork(final List<Object> list) {
    final List<Object> taskList = list;

这意味着taskList 与传入的对象完全相同(称为list)。

其次,这个List 被传递到CustomTimerTask 的构造函数中。

timer.scheduleAtFixedRate(new CustomTimerTask(taskList) {

第三,让我们检查一下构造函数:

private List<Object> list = new ArrayList<Object>();
public CustomTimerTask(List<Object> list) {
    this.list = list;
}

因此,您的 new ArrayList 被丢弃,并替换为参数 list,我们知道它与最初传递给 timerwork()完全相同的对象

我知道你说过:

我最终制作了传入列表的副本

但这在您发布的任何代码中都看不到。此外,我们不知道(因为您还没有发布)timerwork() 的调用代码,但也许它是这样的:

List<Object> list = ....
timerwork(list);
timerwork2(list);

如果是这样,那么我们看到两个计时器任务都在不同线程中的完全相同的对象上运行,这可以解释ConcurrentModificationException

CustomTimerTask 的构造函数中,即使我的诊断不正确,也是一个简单的修复和一个好的做法:

private List<Object> list;
public CustomTimerTask(List<Object> list) {
    // CustomTimerTask gets its own copy to play with
    this.list = new ArrayList<Object>(list);
}

【讨论】:

    猜你喜欢
    • 2013-12-18
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多