简体   繁体   English

对数组列表的并发修改

[英]concurrent modification on arraylist

There are a lot of concurrent mod exception questions, but I'm unable to find an answer that has helped me resolve my issue.有很多并发的 mod 异常问题,但我无法找到帮助我解决问题的答案。 If you find an answer that does, please supply a link instead of just down voting.如果您找到答案,请提供一个链接,而不是仅仅投反对票。

So I originally got a concurrent mod error when attempting to search through an arraylist and remove elements.所以我最初在尝试搜索数组列表并删除元素时遇到了并发 mod 错误。 For a while, I had it resolved by creating a second arraylist, adding the discovered elements to it, then using removeAll() outside the for loop.有一段时间,我通过创建第二个数组列表,将发现的元素添加到其中,然后在 for 循环外使用 removeAll() 来解决它。 This seemed to work, but as I used the for loop to import data from multiple files I started getting concurrent modification exceptions again, but intermittently for some reason.这似乎有效,但是当我使用 for 循环从多个文件导入数据时,我再次开始收到并发修改异常,但由于某种原因间歇性地出现。 Any help would be greatly appreciated.任何帮助将不胜感激。

Here's the specific method having the problem (as well as the other methods it calls...):这是有问题的具体方法(以及它调用的其他方法......):

public static void removeData(ServiceRequest r) {
    readData();
    ArrayList<ServiceRequest> targets = new ArrayList<ServiceRequest>();
    for (ServiceRequest s : serviceQueue) {  
    //ConcurrentModification Exception triggered on previous line
        if ( 
            s.getClient().getSms() == r.getClient().getSms() && 
            s.getTech().getName().equals(r.getTech().getName()) && 
            s.getDate().equals(r.getDate())) {
                JOptionPane.showMessageDialog(null, s.getClient().getSms() + "'s Service Request with " + s.getTech().getName() + " on " + s.getDate().toString() + " has been removed!");
                targets.add(s);
                System.out.print("targetted"); }
    }
    if (targets.isEmpty()) { System.out.print("*"); }
    else { 
        System.out.print("removed");
        serviceQueue.removeAll(targets); 
        writeData(); }
}
public static void addData(ServiceRequest r) {
    readData();
    removeData(r);
    if (r.getClient().getStatus().equals("MEMBER") || r.getClient().getStatus().equals("ALISTER")) {
        serviceQueue.add(r); } 
    else if (r.getClient().getStatus().equals("BANNED") || r.getClient().getStatus().equals("UNKNOWN")) {
        JOptionPane.showMessageDialog(null, "New Request failed: " + r.getClient().getSms() + " is " + r.getClient().getStatus() + "!", "ERROR: " + r.getClient().getSms(), JOptionPane.WARNING_MESSAGE);
    }
    else {
        int response = JOptionPane.showConfirmDialog(null, r.getClient().getSms() + " is " + r.getClient().getStatus() + "...", "Manually Overide?", JOptionPane.OK_CANCEL_OPTION);
        if (response == JOptionPane.OK_OPTION) {
            serviceQueue.add(r); }
    }
    writeData(); }

public static void readData() {
    try {
        Boolean complete = false;
        FileReader reader = new FileReader(f);
        ObjectInputStream in = xstream.createObjectInputStream(reader);
        serviceQueue.clear();
        while(complete != true) {   
            ServiceRequest test = (ServiceRequest)in.readObject();      
            if(test != null && test.getDate().isAfter(LocalDate.now().minusDays(180))) { 
                serviceQueue.add(test); }
                else { complete = true; }
        }
        in.close(); } 
    catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } 
}
public static void writeData() {
    if(serviceQueue.isEmpty()) { serviceQueue.add(new ServiceRequest()); }
    try {
        FileWriter writer = new FileWriter(f);
        ObjectOutputStream out = xstream.createObjectOutputStream(writer);
        for(ServiceRequest r : serviceQueue) { out.writeObject(r); }
        out.writeObject(null);
        out.close(); }
    catch (IOException e) { e.printStackTrace(); }
}

EDIT编辑

The changes cause the concurrent mod to trigger every time rather than intermittently, which I guess means the removal code is better but the error now triggers at it.remove();这些更改导致并发 mod 每次触发而不是间歇性触发,我猜这意味着删除代码更好,但错误现在在it.remove();处触发it.remove();

public static void removeData(ServiceRequest r) {
    readData();
    for(Iterator<ServiceRequest> it = serviceQueue.iterator(); it.hasNext();) {
        ServiceRequest s = it.next();
        if ( 
            s.getClient().getSms() == r.getClient().getSms() && 
            s.getTech().getName().equals(r.getTech().getName()) && 
            s.getDate().equals(r.getDate())) {
                JOptionPane.showMessageDialog(null, s.getClient().getSms() + "'s Service Request with " + s.getTech().getName() + " on " + s.getDate().toString() + " has been removed!");
                it.remove();  //Triggers here (line 195)
                System.out.print("targetted"); }
    }
    writeData(); }

Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificatio nException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at data.ServiceRequest.removeData(ServiceRequest.java:195) at data.ServiceRequest.addData(ServiceRequest.java:209) <...>线程“AWT-EventQueue-0”中的异常 java.util.ConcurrentModificatio nException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851)在 data.ServiceRequest.removeData(ServiceRequest.java:195) 在 data.ServiceRequest.addData(ServiceRequest.java:209) <...>

EDIT After some more searching, I've switch the for loop to:编辑经过更多搜索后,我已将 for 循环切换为:

        Iterator<ServiceRequest> it = serviceQueue.iterator();
        while(it.hasNext()) {

and it's back to intermittently triggering.它又回到间歇性触发。 By that I mean the first time I attempt to import data (the removeData method is being triggered from the addData method) it triggers the concurrent mod exception, but the next try it pushes past the failure and moves on to another file.我的意思是当我第一次尝试导入数据时(removeData 方法是从 addData 方法触发的),它会触发并发 mod 异常,但下一次尝试它会跳过失败并移至另一个文件。 I know there's a lot of these concurrent mod questions, but I'm not finding anything that helps in my situation so links to other answers are more than welcome...我知道有很多这些并发的 mod 问题,但我没有找到任何对我的情况有帮助的东西,所以非常欢迎指向其他答案的链接......

This is not how to do it, to remove elements while going through a List you use an iterator.这不是如何做到的,在使用迭代器遍历 List 时删除元素。 Like that :像那样 :

List<ServiceRequest> targets = new ArrayList<ServiceRequest>();
for(Iterator<ServiceRequest> it = targets.iterator(); it.hasNext();) {
    ServiceRequest currentServReq = it.next();
    if(someCondition) {
        it.remove();
    }
}

And you will not get ConcurrentModificationException this way if you only have one thread.如果您只有一个线程,您将不会以这种方式获得 ConcurrentModificationException。

If there is multiple threads involved in your code, you may still get ConcurrentModificationException.如果您的代码中涉及多个线程,您可能仍会收到 ConcurrentModificationException。 One way to solve this, is to use Collections.synchronizedCollection(...) on your collection (serviceQueue) and as a result you will get a synchronized collection that will not produce ConcurrentModificationException.解决此问题的一种方法是在您的集合 (serviceQueue) 上使用 Collections.synchronizedCollection(...),因此您将获得一个不会产生 ConcurrentModificationException 的同步集合。 But, you code may become very slow.但是,您的代码可能会变得很慢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM