ConcurrentModificationException的这个解决方案是否安全?

时间:2014-06-24 01:24:39

标签: java multithreading concurrentmodification

我有一个第二个线程用于使用OSC发送消息。 从主线程我添加消息,我遇到了ConcurrentModificationException的问题。

我做了什么来修复它是我创建了一个新列表,其中包含要添加的消息。 在第二个线程中,我将这些消息添加到我要发送的列表中。

目前它没有任何问题,但我想知道那是运气吗? 换句话说,可能是遇到ConcurrentModificationException的更改仍然存在,但现在真的很小,还是我真的解决了这个问题?

 public void run() {


    while (running) {

        toSend.addAll(newMessages);
        newMessages.clear();

        Iterator<OSCPriorityMessage> itr = toSend.iterator();

        while (itr.hasNext()) {
            OSCPriorityMessage msg = itr.next();
            oscP5.send(msg, netAddress);
            itr.remove();
        }


        try {
            sleep((long)(waitTime));
        }
        catch (Exception e) {

        }

    }

}

在这里我添加要发送的消息:

public void send(OSCPriorityMessage msg) {


    newMessages.add(msg);
}

1 个答案:

答案 0 :(得分:1)

您仍然需要在访问newMessages时进行同步。我们在这里看到的两个地方是1)添加到它和2)将其复制到toSend然后清除它。也许还有更多。

public void send(OSCPriorityMessage msg) {

    synchronized(newMessages){
       newMessages.add(msg);
    }
}

要明确toSend是一个临时的本地列表,仅用于发送过程,请将其声明为本地变量。

而不是

toSend.addAll(newMessages);
newMessages.clear();

你可以做到

ArrayList<OSCPriorityMessage> toSend;
synchronized(newMessages){
   toSend = new ArrayList<>(newMessages);
   newMessages.clear();
}

如果没有这种同步,你可能会错过一些消息(或者让它们两次,或者可能是一些非常奇怪的东西),因为它们会被同时添加。


现在你在每次循环迭代中都有一个新的toSend,你实际上不再需要删除这些元素,并且可以取消整个迭代器,用一个简单的循环替换它:

for (OSCPriorityMessage msg: toSend){
    oscP5.send(msg, netAddress);
}

最后,正如@dlev建议的那样,看一下java.util.concurrent包中现有的线程安全队列实现。