注意,ArrayList实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedList
方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的remove 或add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException
。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
测试
package my.test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*@author E-mail: xyylchq@163.com
*@version 创建时间:2012-8-15下午02:11:03
*类说明
*/
public class ConcurrentModificationExceptionTest {
public static void main(String[] args) {
final ArrayList<Object> list = new ArrayList<Object>();
for(int i = 0; i < 5; i ++) {
list.add(i);
}
List<Object> list2 = (List)list.clone();
// list.clear();
// System.out.println(list2.size());
Runnable run = new Runnable( ) {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("3秒过后给list赋值");
//添加测试
list.add("sd");
list.add("sd");
//删除测试
// list.clear();
}
};
new Thread(run).start();
Iterator<Object> iterator = list.iterator();
// for(int i = 0; i < list.size(); i++) {
while(iterator.hasNext()) {
System.out.println(iterator.next());
// System.out.println(list.get(i));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
解决方法:
1. 使用 Collections.synchronizedList
方法将该列表“包装”起来。
2. 将要创建的ArrayList创建一个副本,clone出一个副本。
这里还需要注意的是 Collections.synchronizedList这个方法的使用。
下面是转帖内容:
private List<JobInfo> mJobList;
mJobList = Collections.synchronizedList(new ArrayList<JobInfo>());
这种写法是正确的,因为synchronizedList方法的返回值就是List集合的,但是看看下面这种写法
private ArrayList<JobInfo> mJobList;
mJobList = (ArrayList<JobInfo>)Collections.synchronizedList(new ArrayList<JobInfo>());
这种写法看起来时正确的,实际上会报异常“Caused by: java.lang.ClassCastException: java.util.Collections$SynchronizedRandomAccessList”
分析一下:
1. 函数的<T> List<T>是什么意思?
我知道的是返回一个List类型的集合,它里面存储的对象的类型是T,但是最前面的那个<T>又是什么意思呢?
在这个泛型方法中,类型参数是T,它位于函数的所有的修饰符之后,返回值之前,放在尖括号中,这个T是该函数的类型参数,而List<T>中的T是这个集合的类型参数
List<T>:参数化类型的返回值。
接下来让我们看看synchronizedList方法的源码:
public static <T> List<T> synchronizedList(List<T> list) {
if (list == null) {
throw new NullPointerException();
}
if (list instanceof RandomAccess) {
return new SynchronizedRandomAccessList<T>(list);
}
return new SynchronizedList<T>(list);
}
mJobList = (ArrayList<JobInfo>)Collections.synchronizedList(new ArrayList<JobInfo>());这行代码中,给synchronizedList方法传递了一个ArrayList类型的参数,ArrayList实现了RandomAccess接口,所以
会去执行return new SynchronizedRandomAccessList<T>(list);这行代码,这时我们来看看uml类图,其中SynchronizedRandomAccessList,SynchronizedList,SynchronizedCollection这三个类都是Collections工具类的static内部类
通过这个图我们可以看到,SynchronizedRandomAccessList是List的子类,他不是ArrayList类型的,因此上面代码的强转是不对的,此外我们还可以看出这是一个包装模式,关于包装模式以后再写。