对数组列表的并发修改 [英] concurrent modification on arraylist

查看:33
本文介绍了对数组列表的并发修改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多并发的 mod 异常问题,但我无法找到帮助我解决问题的答案.如果你找到了答案,请提供一个链接,而不是仅仅投反对票.

There are a lot of concurrent mod exception questions, but I'm unable to find an answer that has helped me resolve my issue. If you find an answer that does, please supply a link instead of just down voting.

所以我最初在尝试搜索数组列表并删除元素时遇到了并发 mod 错误.有一段时间,我通过创建第二个数组列表,将发现的元素添加到其中,然后在 for 循环外使用 removeAll() 来解决它.这似乎有效,但是当我使用 for 循环从多个文件导入数据时,我再次开始收到并发修改异常,但由于某种原因间歇性地出现.任何帮助将不胜感激.

So I originally got a concurrent mod error when attempting to search through an arraylist and remove elements. 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. 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. 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(); }
}

编辑

更改导致并发 mod 每次触发而不是间歇性触发,我猜这意味着删除代码更好,但错误现在在 it.remove();

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();

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(); }

线程AWT-EventQueue-0"中的异常 java.util.ConcurrentModificatio异常在 java.util.ArrayList$Itr.checkForCommodification(ArrayList.java:901)在 java.util.ArrayList$Itr.next(ArrayList.java:851)在 data.ServiceRequest.removeData(ServiceRequest.java:195)在 data.ServiceRequest.addData(ServiceRequest.java:209) <...>

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) <...>

编辑经过更多搜索后,我将 for 循环切换为:

EDIT After some more searching, I've switch the for loop to:

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

它又回到了间歇性触发的状态.我的意思是当我第一次尝试导入数据时(removeData 方法是从 addData 方法触发的),它触发了并发 mod 异常,但下一次尝试它会克服失败并移动到另一个文件.我知道有很多这些并发的 mod 问题,但我没有找到任何对我的情况有帮助的东西,所以非常欢迎指向其他答案的链接......

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. 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...

推荐答案

这不是怎么做的,在使用迭代器遍历 List 时删除元素.像这样:

This is not how to do it, to remove elements while going through a List you use an iterator. Like that :

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

如果您只有一个线程,您将不会以这种方式获得 ConcurrentModificationException.

And you will not get ConcurrentModificationException this way if you only have one thread.

如果您的代码中涉及多个线程,您仍然可能会收到 ConcurrentModificationException.解决此问题的一种方法是在您的集合 (serviceQueue) 上使用 Collections.synchronizedCollection(...),因此您将获得一个不会产生 ConcurrentModificationException 的同步集合.但是,您的代码可能会变得很慢.

If there is multiple threads involved in your code, you may still get 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. But, you code may become very slow.

这篇关于对数组列表的并发修改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆