java中集合框架中的类多数都是不同步的,如ArrayList,LinkedList,如果需要在多线程中同步需要使用Collections.synchronizedList,使用中经常出现ConcurrentModificationException 的异常情况,举例说明
public class EventObserver {
private static class EventObseverHolder {
private static final EventObserver sInstance = new EventObserver();
}
private List<EventListener> mList = Collections.synchronizedList(new ArrayList<EventListener>());
public static EventObserver getInstance() {
return EventObseverHolder.sInstance;
}
public void addEventListener(EventListener listener) {
if (null == listener) {
return;
}
synchronized (mList) {
if (mList.contains(listener)) {
return;
}
mList.add(listener);
}
}
public void removeEventListener(EventListener listener) {
if (null == listener) {
return;
}
mList.remove(listener);
}
public void sendEvent() {
synchronized (mList) {
Iterator<EventListener> iterator = mList.iterator();
while (iterator.hasNext()) {
EventListener listener = iterator.next();
listener.onEvent();
}
}
}
interface EventListener {
void onEvent();
}
}
上面代码本已实现列表同步,但是当出现onEvent中调用removeEventListener情况就会导致ConcurrentModificationException 异常,当然多数人应该不会直接出现这种情况,这种一般出现在间接调用中,比较难发现,如果出现这样问题,可以梳理一下调用流程。
ConcurrentModificationException 这个异常是怎么出现的,下面给出ArrayList源码出处
public Iterator<E> iterator() {
return new Itr();
}
下面是迭代器初始化一些东西
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
关键这个初始化,
int expectedModCount = modCount;
public E next() {
遍历时候检查如下
checkForComodification();
.....
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
当在列表遍历的时候,移除其中一项(非迭代器移除如removeEventListener中的移除),将导致modCount 这个值发生变化,expectedModCount不会被修改,导致这两个值不会相等
EventObserver 这个能否在多线程同步呢?答案是肯定的,EventObserver 中同步代码块锁的对象跟列表原子操作是一样的,请看源码:
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<T>(list) :
new SynchronizedList<T>(list));
}
ArrayList继承了RandomAccess接口,所以这里返回SynchronizedRandomAccessList对象,接下来看SynchronizedRandomAccessList
static class SynchronizedRandomAccessList<E>
extends SynchronizedList<E>
implements RandomAccess {
SynchronizedRandomAccessList(List<E> list) {
super(list);
}
SynchronizedRandomAccessList(List<E> list, Object mutex) {
super(list, mutex);
}
SynchronizedRandomAccessList又继承了SynchronizedList,看父类SynchronizedList
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
.....
}
SynchronizedList父类SynchronizedCollection,其中的锁是mutex对象
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
mutex = this;
}
SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = c;
this.mutex = mutex;
}
....
public int size() {
synchronized(mutex) {return c.size();}
}
....
}
mutex = this;表明同步列表原子操作锁对象与EventObserver 中同步代码块锁的对象是相同的。
很少去写一些东西,难免有错误,还请大家指正,谢谢!