Why am I still getting ConcurrentModificationException?!
Here is a simple test that I ran on a collection that I have implemented, in which I try to add multiple objects to the collection in multiple threads. I have labelled the methods which modify the colleciton as synchronized, so I had thought it would not allow more than one LinkedCollection.add operation at a time. However, I am getting ConcurrentModificationExceptions from it. Below the test I have included the code for my LinkedCollection class and all other relevant classes, so that it's possible for anyone here to run it if needed. Thanks for any help!
==========================================================================================
publicclass ThreadTest{
publicstaticvoid main(String[] args){
LinkedCollection<Integer> c =new LinkedCollection<Integer>();
for (int i = 0; i < 10; i++){
Thread t =new Thread(new Berk(c, i * 10),"Thread " + i);
t.start();
}
System.out.println(c);
}
staticclass Berkimplements Runnable{
private LinkedCollection<Integer> c;
privateint i;
public Berk(LinkedCollection<Integer> c,int i){
this.c = c;
this.i = i;
}
publicvoid run(){
for (int i = 0; i < 10; i++)
c.add(this.i + i);
}
}
}
==========================================================================================
import java.util.Collection;
publicclass LinkedCollection<E>implements Collection<E>, PublicCloneable<LinkedCollection><E>>, ModificationEventGenerator{
protected Position<E> head;
protectedint size;
private LinkedCollection<ModificationListener> listeners;
public LinkedCollection(){
this(true);
}
protected LinkedCollection(boolean notifyModifications){
listeners = notifyModifications ?new LinkedCollection<ModificationListener>(false) :null;
}
publicboolean isEmpty(){
return head ==null;
}
publicboolean contains(Object element){
return getFirstPosition(element) !=null;
}
publicboolean containsAny(Collection<?> c){
for (Object element : c)
if (contains(element))
returntrue;
returnfalse;
}
publicboolean containsAll(Collection<?> c){
for (Object element : c)
if (!contains(element))
returnfalse;
returntrue;
}
publicint occurrences(Object element){
return getPositions(element).size();
}
publicint size(){
return size;
}
publicsynchronizedboolean add(E element){
if (element !=null){
generateModificationEvent();
if (isEmpty())
head =new Position<E>(element);
else
head.prev = head.prev.next =new Position<E>(element, head.prev, head);
size++;
returntrue;
}
returnfalse;
}
publicsynchronizedboolean addAll(Collection<?extends E> c){
boolean changed =false;
for (E element : c)
if (add(element))
changed =true;
return changed;
}
publicsynchronizedboolean remove(Object element){
LinkedCollection<Position><E>> removals = getPositions(element);
for (Position<E> current : removals){
if (size == 1)
head =null;
else{
if (current == head)
head = head.next;
current.prev.next = current.next;
current.next.prev = current.prev;
current.prev = current.next = current;
}
size--;
}
return generateModificationEvent(!removals.isEmpty());
}
publicsynchronizedboolean removeAll(Collection<?> c){
boolean changed =false;
for (Object element : c)
if (remove(element))
changed =true;
return changed;
}
publicsynchronizedboolean retainAll(Collection<?> c){
LinkedCollection<E> removals =new LinkedCollection<E>();
for (E element :this)
if (!c.contains(element))
removals.add(element);
for (E element : removals)
this.remove(element);
return !removals.isEmpty();
}
publicsynchronizedvoid clear(){
Position next;
while (size > 0){
next = head.next;
head.prev = head.next = head;
head = next;
size--;
}
head =null;
if (listeners !=null)
listeners.clear();
}
public Object[] toArray(){
Object[] array =new Object[size];
int index = 0;
for (E element :this)
array[index++] = element;
return array;
}
public <T> T[] toArray(T[] type){
T[] array = type.length < size ? (T[])new Object[size] : type;
int index = 0;
for (E element :this)
array[index++] = (T)element;
if (array.length > size)
array[index] =null;
return array;
}
public String toString(){
String s ="";
for (E element :this)
s += element.toString() +" ";
return s;
}
publicboolean equals(Object o){
if (!(oinstanceof Collection<?>))
returnfalse;
LinkedCollection<?> s = (LinkedCollection<?>)o;
for (Object element : s)
if (occurrences(element) != s.occurrences(element))
returnfalse;
for (E element :this)
if (!s.contains(element))
returnfalse;
returntrue;
}
publicint hashCode(){
return 0;
}
public LinkedCollection<E> clone(){
LinkedCollection<E> clone =new LinkedCollection<E>();
clone.addAll(this);
return clone;
}
publicvoid addModificationListener(ModificationListener m){
if (listeners !=null && !listeners.contains(m))
listeners.add(m);
}
publicvoid removeModificationListener(ModificationListener m){
if (listeners !=null)
listeners.remove(m);
}
publicvoid generateModificationEvent(){
if (listeners !=null)
for (ModificationListener m : listeners)
m.modified(new ModificationEvent(this));
}
publicboolean generateModificationEvent(boolean condition){
if (condition)
generateModificationEvent();
return condition;
}
publicfinal java.util.Iterator<E> iterator(){
returnnew Iterator<E>(this, head, head);
}
protected LinkedCollection<Position><E>> getPositions(Object element){
LinkedCollection<Position><E>> matches =new LinkedCollection<Position><E>>(false);
if (!isEmpty()){
Position<E> match = head.prev;
int count = 0;
while (matches.add(match.next == head && count++ > 0 ?null : (match = getFirstPosition(element, match.next))));
}
return matches;
}
protected Position<E> getFirstPosition(Object element, Position<E> start){
if (!isEmpty()){
Iterator i =new Iterator(this, start, head);
while (i.hasNext())
if (i.next().equals(element)){
i.terminate();
return i.currentPosition();
}
}
returnnull;
}
protected Position<E> getFirstPosition(Object element){
return getFirstPosition(element, head);
}
}
==========================================================================================
import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException;
class Iterator<E>implements java.util.Iterator<E>, ModificationListener{
private ModificationEventGenerator parent;
protected Position<E> start, end, current, next;
privateboolean started, hasNext, hasNextCalculated, terminatedExternally;
public Iterator(ModificationEventGenerator parent, Position<E> start, Position<E> end){
this.parent = parent;
parent.addModificationListener(this);
this.start = start;
this.end = end;
current =null;
next = start;
}
publicboolean hasNext(){
checkTerminatedExternally();
if (!hasNextCalculated){
hasNext = !(start ==null || (started && (next ==null || next == end)));
hasNextCalculated =true;
}
if (!hasNext)
terminate();
return hasNext;
}
public E next(){
return nextPosition().element();
}
protected Position<E> nextPosition(){
checkTerminatedExternally();
if (!hasNext())
thrownew NoSuchElementException();
started =true;
hasNextCalculated =false;
next = (current = next).next;
return current;
}
protected Position<E> currentPosition(){
return current;
}
publicvoid remove(){
thrownew UnsupportedOperationException();
}
publicvoid modified(ModificationEvent e)throws ConcurrentModificationException{
if (e.getSource() == parent)
thrownew ConcurrentModificationException();
}
publicvoid terminate(){
terminate(true);
}
privatevoid terminate(boolean externally){
checkTerminatedExternally();
parent.removeModificationListener(this);
if (externally)
terminatedExternally =true;
}
privatevoid checkTerminatedExternally(){
if (terminatedExternally)
thrownew UnsupportedOperationException("It is not possible to use an iterator that has been terminated.");
}
}
==========================================================================================
import java.util.Iterator;
publicclass Position<E>{
private E element;
protected Position prev = this, next =this;
public Position(E element){
this.element = element;
}
public Position(E element, Position prev, Position next){
this(element);
this.prev = prev;
this.next = next;
}
public E element(){
return element;
}
public String toString(){
return"[" + prev.element.toString() +"]<[" + element.toString() +"]>[" + next.element.toString() +"] ";
}
}
==========================================================================================
import java.util.EventListener;
publicinterface ModificationListenerextends EventListener{
publicvoid modified(ModificationEvent e);
}
==========================================================================================
import java.util.EventObject;
publicclass ModificationEventextends EventObject{
public ModificationEvent(Object source){
super(source);
}
}
==========================================================================================
publicinterface ModificationEventGenerator{
publicvoid addModificationListener(ModificationListener m);
publicvoid removeModificationListener(ModificationListener m);
publicvoid generateModificationEvent();
}
==========================================================================================
publicinterface PublicCloneable<Textends PublicCloneable><T>>{
public T clone();
}
==========================================================================================

