Java 引发ConcurrentModificationException的ArrayList迭代器
我有一个带有两个访问器方法和一个通知器的ArrayList。我的名单:Java 引发ConcurrentModificationException的ArrayList迭代器,java,loops,arraylist,concurrency,iterator,Java,Loops,Arraylist,Concurrency,Iterator,我有一个带有两个访问器方法和一个通知器的ArrayList。我的名单: private final List<WeakReference<LockListener>> listeners = new ArrayList<>(); private final List listeners=new ArrayList(); 所有订阅操作都使用此选项: public void subscribe(@NonNull LockListener listener) {
private final List<WeakReference<LockListener>> listeners = new ArrayList<>();
private final List listeners=new ArrayList();
所有订阅操作都使用此选项:
public void subscribe(@NonNull LockListener listener) {
for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext(); ) {
// has this one already subscribed?
if (listener.equals(it.next().get())) {
return;
}
}
listeners.add(new WeakReference<>(listener));
}
public void unsubscribe(@NonNull LockListener listener) {
if (listeners.isEmpty()) {
return;
}
for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext(); ) {
WeakReference<LockListener> ref = it.next();
if (ref == null || ref.get() == null || listener.equals(ref.get())) {
it.remove();
}
}
}
public void subscribe(@NonNull LockListener){
for(Iterator it=listeners.Iterator();it.hasNext();){
//这个已经订阅了吗?
if(listener.equals(it.next().get())){
返回;
}
}
add(newweakreference(listener));
}
所有取消订阅操作都使用此选项:
public void subscribe(@NonNull LockListener listener) {
for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext(); ) {
// has this one already subscribed?
if (listener.equals(it.next().get())) {
return;
}
}
listeners.add(new WeakReference<>(listener));
}
public void unsubscribe(@NonNull LockListener listener) {
if (listeners.isEmpty()) {
return;
}
for (Iterator<WeakReference<LockListener>> it = listeners.iterator(); it.hasNext(); ) {
WeakReference<LockListener> ref = it.next();
if (ref == null || ref.get() == null || listener.equals(ref.get())) {
it.remove();
}
}
}
public void取消订阅(@NonNull LockListener listener){
if(listeners.isEmpty()){
返回;
}
for(Iterator it=listeners.Iterator();it.hasNext();){
WeakReference ref=it.next();
如果(ref==null | | ref.get()==null | | listener.equals(ref.get())){
it.remove();
}
}
}
以及通知者:
private void notifyListeners() {
if (listeners.isEmpty()) {
return;
}
Iterator<WeakReference<LockListener>> it = listeners.iterator();
while (it.hasNext()) {
WeakReference<LockListener> ref = it.next();
if (ref == null || ref.get() == null) {
it.remove();
} else {
ref.get().onLocked();
}
}
}
private void notifyListeners(){
if(listeners.isEmpty()){
返回;
}
Iterator it=listeners.Iterator();
while(it.hasNext()){
WeakReference ref=it.next();
if(ref==null | | ref.get()==null){
it.remove();
}否则{
ref.get().onLocked();
}
}
}
我在测试中看到,notifyListeners()中的it.next()偶尔会抛出ConcurrentModificationException。我猜这是由于订阅服务器方法中的listeners.add()造成的
我想我对迭代器有误解。我的假设是,对列表进行迭代可以保护我不受添加/删除操作引起的并发问题的影响
显然我错了。是否迭代器只是在更改正在迭代的集合时防止ConcurrentModificationException的一种保护?例如,在迭代时对列表调用remove()会引发错误,但调用它.remove()是安全的
在我的例子中,订阅在迭代的同一个列表上调用add()。我在这里的理解正确吗?如果我正确地阅读了您的最后一句话,那么您示例中的三个方法将从多个线程并发调用。如果确实如此,那么这就是你的问题 ArrayList不是线程安全的。无论是直接修改还是使用迭代器修改,在没有额外同步的情况下并发修改它都会导致未定义的行为
您可以同步对列表的访问(例如,使三个方法同步),或者使用线程安全的集合类,如ConcurrentLinkedQue。对于后者,请务必阅读JavaDoc(特别是关于迭代器每周一致性的部分),以了解什么是有保证的,什么是没有保证的。如果您阅读迭代器的文档,它将告诉您不能修改底层结构