同步容器
在Java中,同步容器主要包括2类:
- Vector、Stack、HashTable。Vector实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。Stack也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。HashTable实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。
- Collections类中提供的静态工厂方法创建的类。
Collections.synchronizedCollection(Collection<T> c)
Collections.synchronizedSet(Set<T> s)
Collections.synchronizedList(List<T> list)
Collections.synchronizedMap(Map<K,V> m)
同步容器的缺陷:
- 从同步容器的具体实现源码可知,同步容器中的方法采用了synchronized进行了同步,这必然会影响到执行性能。
- 同步容器并不能保证所有操作都是现成安全的。
- 在对Vector等容器并发地进行迭代修改时,会报ConcurrentModificationException异常。
ConcurrentModificationException异常
对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。即:
public class Test {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2));
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer == 2)
list.remove(integer);
}
}
}
结果抛出异常:java.util.ConcurrentModificationException
同样
for(Integer i: list){
if(2 == i){
list.remove(i);
}
}
也会抛出异常。
但是有一点,java7下,要修改的元素不是list的最后一位则不会抛异常,不懂。
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer == 2)
list.remove(integer);
}
上面这种情况不会抛出异常。
ConcurrentModificationException 异常原因
// TODO 分析源码
原因:调用list.remove()方法导致modCount和expectedModCount的值不一致。
单线程下解决办法
(1)调用Iterator.remove()
public class Test {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2));
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer == 2)
iterator.remove(); //注意这个地方
}
}
}
(2)额外使用list保存要删除的元素
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1,2));
Iterator<Integer> iterator = list.iterator();
ArrayList<Integer> toRemove = new ArrayList<>();
while (iterator.hasNext()){
Integer integer = iterator.next();
if(integer == 2){
toRemove.add(integer);
}
}
list.removeAll(toRemove);
多线程下解决办法
上述方法中的第一种,在多线程下并不适用。不管使用的是ArrayList还是线程安全的Vector,都可能会抛出异常。
通常的两种解决办法:
- 在使用iterator迭代的时候使用synchronized或者Lock进行同步;
- 使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。