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();

}

==========================================================================================

[25080 byte] By [ProGrapea] at [2007-11-27 5:49:46]
# 1
Please help!
ProGrapea at 2007-7-12 15:36:45 > top of Java-index,Core,Core APIs...
# 2

I have changed my tests to call Collections.synchronizedCollection when initialising the collection, but now, although the exceptions no longer occur, hardly any of the elements are being added to the collection! Here is my new test code:

import java.util.Collection;

import java.util.Collections;

public class GeneralTests {

public static void main(String[] args) {

Collection<Integer> c = Collections.synchronizedCollection(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);

}

static class Berk implements Runnable {

private Collection<Integer> c;

private int i;

public Berk(Collection<Integer> c, int i) {

this.c = c;

this.i = i;

}

public void run() {

for (int i = 0; i < 10; i++)

c.add(this.i + i);

}

}

}

ProGrapea at 2007-7-12 15:36:45 > top of Java-index,Core,Core APIs...
# 3
> hardly any of the elements are being added to the collection!Well, you do print the collection without waiting for the threads to finish.
DrClapa at 2007-7-12 15:36:45 > top of Java-index,Core,Core APIs...
# 4
Hehe, yes it's true, thanks for your response. I've all but figured it out in the last few hours. :)
ProGrapea at 2007-7-12 15:36:45 > top of Java-index,Core,Core APIs...