简体   繁体   English

迭代器的并发修改异常

[英]concurrent modification exception with iterator

I'm fairly new to java and I'm essentially writing a keylogger and having it write to file at regular intervals. 我对Java相当陌生,实际上是在编写键盘记录程序,并使其定期写入文件。 Everytime a user presses a key it instantiates a NativeKeyEvent which invokes 'paramString()' and adds the information as a string to the arraylist below... 每当用户按下一个键时,它都会实例化一个NativeKeyEvent,该NativeKeyEvent调用“ paramString()”并将信息作为字符串添加到下面的arraylist中。

public static ArrayList<String> stringArray = new ArrayList<String>();

public synchronized String paramString() {
    StringBuilder param = new StringBuilder(255);
    // other code
    stringArray.add(param.toString());
}

Then at each interval the string array is passed across and is written to file in the TimerTask thread below... 然后,在每个间隔处传递字符串数组,并将其写入下面的TimerTask线程中的文件...

public class SaveToArrayAndWriteTask extends TimerTask {

private ArrayList<String> anotherArray = NativeKeyEvent.stringArray;
private static String str;

   @Override
   public synchronized void run() {
     openFile();
     writeToFile(anotherArray);
     closeFile();
   }

   private static synchronized void writeToFile(ArrayList<String> localArray) {

    Iterator<String> iterator = localArray.iterator();
    try {
        while (iterator.hasNext()) {
            str = iterator.next().toString();
            output.format("%s\n", str);
        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

The line 'str = iterator.next().toString();' 这行'str = iterator.next()。toString();' then throws an exception if it attempts to iterate through the arraylist to write it to file while the program is recording / adding another key stroke. 如果在程序录制/添加另一个按键时尝试遍历arraylist并将其写入文件,则抛出异常。 I thought by putting stringArray (which is being modified) into anotherArray and passing it as an argument would stop this from happening. 我认为通过将stringArray(正在被修改)放入anotherArray并将其作为参数传递将阻止这种情况的发生。 As you can see I've tried using the synchronized keyword and I've also tried putting it into another thread and have read various other posts on stackoverflow but to no avail. 如您所见,我已经尝试过使用synced关键字,并且还尝试将其放入另一个线程中,并阅读了关于stackoverflow的其他各种文章,但无济于事。

What would be the best way to solve this issue? 解决此问题的最佳方法是什么?

You aren't making a copy of the ArrayList, you are just making another reference to the same ArrayList. 您不是在复制ArrayList,而是在另一个引用相同的ArrayList。

private ArrayList<String> anotherArray = NativeKeyEvent.stringArray;

Should be something like: 应该是这样的:

private ArrayList<String> copy = new ArrayList<>(NativeKeyEvent.stringArray);

This will create a new ArrayList with the same elements as NativeKeyEvent.stringArray . 这将创建一个数组ArrayList,其元素与NativeKeyEvent.stringArray相同。

I would recommend using a getter instead of exposing stringArray as a public variable. 我建议使用getter而不是将stringArray公开为公共变量。 The getter should make a copy of the stringArray and return that instead. getter应该复制stringArray并返回它。 That way any consumer of stringArray will operate on a copy and won't have the possibility of modifying the list. 这样, stringArray任何使用者都将在副本上进行操作,并且无法修改列表。

I thought by putting stringArray (which is being modified) into anotherArray 我想通过将stringArray(正在被修改)放入anotherArray

private ArrayList<String> anotherArray = NativeKeyEvent.stringArray;

Is not creating a new ArrayList. 没有创建新的ArrayList。 anotherArray is still referencing the same object. anotherArray仍在引用同一对象。 You need to create a new ArrayList that contains the elements of stringArray. 您需要创建一个新的ArrayList,其中包含stringArray的元素。

private ArrayList<String> anotherArray = new ArrayList<>(NativeKeyEvent.stringArray);

Generally speaking, if you modify a collection while iterating over it, you'll get a ConcurrentModificationException . 一般而言,如果在迭代集合时对其进行修改,则会收到ConcurrentModificationException

You have two choices: 您有两种选择:

The hard way: synchronize access to the list so other threads block while you're iterating 困难的方法:同步对列表的访问,以便其他线程在迭代时阻塞

The easy way: Use the CopyOnWriteArrayList list implementation from the java.util.concurrent package, which does all the hard work for you 简单的方法:使用java.util.concurrent包中的CopyOnWriteArrayList列表实现,它为您完成了所有艰苦的工作

public static ArrayList<String> stringArray = new CopyOnWriteArrayList<String>();

That's it. 而已。 Nothing more needed, and you can remove all synchronization. 不再需要,您可以删除所有同步。

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

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