我目前正试图学习如何正确处理对Collections的多线程访问,所以我写了下面的Java应用程序。又一个ConcurrentModificationException问题
正如你所看到的,我创建了一个同步的ArrayList,我尝试从一个线程中访问一次,一次没有。
我使用for循环遍历ArrayList。为了防止同时在List上多次访问,我将循环封装到一个同步块中。
public class ThreadTest {
Collection<Integer> data = Collections.synchronizedList(new ArrayList<Integer>());
final int MAX = 999;
/**
* Default constructor
*/
public ThreadTest() {
initData();
startThread();
startCollectionWork();
}
private int getRandom() {
Random randomGenerator = new Random();
return randomGenerator.nextInt(100);
}
private void initData() {
for (int i = 0; i < MAX; i++) {
data.add(getRandom());
}
}
private void startCollectionWork() {
System.out.println("\nStarting to work on data outside of thread");
synchronized (data) {
System.out.println("\nEntered synchronized block outside of thread");
for (int value : data) { // ConcurrentModificationException here!
if (value % 5 == 1) {
System.out.println(value);
data.remove(value);
data.add(value + 1);
} else {
System.out.println("value % 5 = " + value % 5);
}
}
}
System.out.println("Done working on data outside of thread");
}
private void startThread() {
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("\nStarting to work on data in a new thread");
synchronized (data) {
System.out.println("\nEntered synchronized block in thread");
for (int value : data) { // ConcurrentModificationException
if (value % 5 == 1) {
System.out.println(value);
data.remove(value);
data.add(value + 1);
} else {
System.out.println("value % 5 = " + value % 5);
}
}
}
System.out.println("Done working on data in a new thread");
}
};
thread.start();
}
}
但是每次for循环都进入,我得到一个ConcurrentModificationException异常。这是我的控制台输出(每改变一次都会改变):
Starting to work on data outside of thread
Entered synchronized block outside of thread
51
Starting to work on data in a new thread
Entered synchronized block in thread
value % 5 = 2
value % 5 = 2
value % 5 = 4
value % 5 = 3
value % 5 = 2
value % 5 = 2
value % 5 = 0
21
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at ThreadTest.startCollectionWork(ThreadTest.java:50)
at ThreadTest.<init>(ThreadTest.java:32)
at MultiThreadingTest.main(MultiThreadingTest.java:18)
Exception in thread "Thread-1" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at ThreadTest$1.run(ThreadTest.java:70)
怎么了?
Ps:请不要只发布链接到多线程的方法,因为我已经阅读了足够多。我只是好奇为什么我的应用程序没有按照我的要求运行。
更新:我取代了对(X:Y)的语法具有显式迭代器和一个while循环。这个问题仍然是虽然..
synchronized(data){
Iterator<Integer> i = data.iterator();
while (i.hasNext()) {
int value = i.next(); // ConcurrentModificationException here!
if (value % 5 == 1) {
System.out.println(value);
i.remove();
data.add(value + 1);
} else {
System.out.println("value % 5 = " + value % 5);
}
}
}
很好的答案!非常感谢。在同一时间迭代副本并修改原始集合解决了问题! – Timo 2011-06-09 14:18:13
有一个角落案件可以移除。一些迭代器实现'iterator.remove()'操作,它允许迭代器(每个元素只有一次)移除当前元素。由于迭代器知道它已经呈现了当前元素,因此它不会担心集合修改。但要注意的一点是,'iterator.remove()'是迭代器的可选操作。换句话说,一些迭代器(对于特定的集合)每次调用'iterator.remove()'时都会抛出'OperationNotSupported'异常。 – 2011-06-09 14:24:36
另外,调用一个迭代器的remove方法仍然可能导致另一个打开的迭代器抛出ConcurrentModificationException,因此remove方法对异常并不完全安全。 – Bhaskar 2011-06-09 14:29:48