一个工具类中有个监听集合(LinkedList listeners),添加和删除都通过synchronized的regist和unregist方法处理,但是在对其监听集合进行synchronized 的 for循环处理数据时还是出现了IndexOutOfBoundsException,百思不得其解。
public abstract class MapLocation {
private LinkedList<ILocationListener> listeners = new LinkedList<ILocationListener>();
public boolean registLocationListener(ILocationListener listener) {
if (listener == null) {
return false;
}
synchronized (listeners) {
if (listeners.contains(listener)) {
return false;
}
listeners.add(listener);
return true;
}
}
public boolean unregistLocationListener(ILocationListener listener) {
if (listener == null) {
return false;
}
synchronized (listeners) {
return listeners.remove(listener);
}
}
protected void dispatchLocation(final LocationEntry le) {
synchronized (listeners) {
int size = listeners.size();
// for (int i = 0; i < size; i++) { //1
for (int i = size - 1; i > 0; i--) {//3
listeners.get(i).onLocationReceive(null, le);
}
}
}
protected void dispatchLocationFailed() {
synchronized (listeners) {
int size = listeners.size();
// for (int i = 0; i < size; i++) {//2
for (int i = size - 1; i > 0; i--) {//4
listeners.get(i).onLocationFailed();
}
}
}
public static interface ILocationListener {
public void onLocationReceive(String fromHID, LocationEntry le);
void onLocationFailed();
}
}
既然出现了IndexOutOfBoundsException,说明监听集合在循环过程中肯定被修改了,导致。既然已经同步,说明修改不可能其他线程在循环外部,只可能在循环的内部出现。最后终于发现在listener的实现中会调用unregist方法将当前listener从集合中remove。而循环中是从头到尾(1,2)获取每个数据项目,并从集合中remove掉自己,size是提前获取好的。这样导致集合中的数据跟size对不上。从而出现越界异常。
解决:如果循环从尾到头获取,虽然尾部的数据项目被remove掉,但是下次循环的index总是从上次remove掉的上一个,因此并无影响。
注意:如果删除时同一次循环同时删除多个数据项目。则会导致下一次循环取数据时拿不到数据报空指针异常,此时解决方法只有一个在另外的线程中删除(Iterator 或者自建线程。Iterator其实是在另外的线程中运行)。
网友评论