If you're bound to use an ArrayList you have to synchronize access to it. But that still doesn't save you from ConcurrentModificationExeptions as this doen't necessarily has something to do with concurrency.
From JavaDoc
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
If you can ensure, only new items get appended to the list, you may just use an index and loop manually over it and not use an iterator. Once you reach the end of the queue, you start all over.
ArrayList q = new ArrayList<>();
//thread 1
Object o = ...;
synchronized(q) {
q.add(o); //append
}
//thread 2
int i = 0;
synchronized(q) {
int size = q.size();
for(; i < size; i++){
Object o = q.get(i);
//do something with o
}
if(i >= size) {
i = 0;
}
}
But doing it that way will lead you to a somewhat sequential behavior as either one or the other thread may operate at once on the list. The only "advantage" is that the threading model adds some randomness to the loop operation. So you could as well skip synchronization and concurrency and just do
q.add(o); //append
//you may add a random condition, i.e. time interval, item count random number to trigger the loop process so it don't get exectued on each add
for(Object o : q){
//do something with o
}
Of course you can use a Safe-Copy as Nicolas wrote, but that's just a shallow copy of the container structure (list) and not of it's contents. So if you do that, be sure the items are thread safe or don't modify them.
In case the second thread occasionally removes an item from the end of list, you should better use a Queue. Java provides the Deque for that. One thread may add elements at one end while the other removes from the other. As you use it in multiple threads, and synchronizing manually sucks, you should better use a thread safe implementation and skip synchronization:
Deque q = new ConcurrentLinkedDeque<>();
//thread 1
Object o = ...;
q.addFirst(o);
//thread 2
while(!q.isEmpty()){
Object o = q.removeLast();
//do something with o
}
Vectorinstead ofArayList