ConcurrentModificationException in java.util.ServiceLoader (not a multi-thread issue)
Peter Levart
peter.levart at gmail.com
Tue Feb 24 12:49:51 UTC 2015
On 02/24/2015 10:32 AM, Martin Desruisseaux wrote:
> Le 24/02/15 09:09, Alan Bateman a écrit :
>
>> Right, it has never supported multiple iterators but as it's an
>> Iterable then it should unless specified otherwise. So I think this is
>> a bug (although one could argue that the usage is unusual, almost a
>> mis-use).
> One use case is that in a multi-thread environment, this bug implies
> that it is not sufficient to synchronize all direct and indirect
> (through Iterator) usages of ServiceLoader. We must synchronize the
> entire iteration loop for preventing other threads to start a new
> iteration before we finished the current one. This can be annoying if
> the work to do between two calls to Iterator.hasNext() / next() is long.
> Sometime it is not possible to synchronize the entire loop if we do not
> control the iteration (i.e. if we advance in the iterator only when the
> user invokes some method). The later case can be a problem even in
> mono-thread application.
>
>
>> Not clear whether it's worth doing anything about it now, this is
>> because ServiceLoader is going to change very significantly soon when
>> it is re-specified to work with modules. I'll create a bug anyway to
>> track it anyway.
> Thanks. The fact that ServiceLoader was going to be modified for Jigsaw
> is precisely the reason why I was wondering if it was worth to
> investigate more now.
>
> Martin
>
>
You could synchronize on the entire loop and just copy over the service
objects to another collection (say ArrayList). Then use the ArrayList
freely from multiple threads just to iterate it without any
synchronization. This will work if constructing service objects is
relatively cheap and you can do them all at once. When using
ServiceLoader for multiple (sequential) iterations, it does the same:
the 2nd and subsequent iterations will just return the same objects from
1st iteration. If you really must combine laziness of constructing
service objects with concurrent iterations, you can wrap the
ServiceLoader.iterator() with the following:
public class BufferedConcurrentIterable<E> implements Iterable<E> {
private final Iterator<E> source;
private final List<E> buffer = new ArrayList<>();
public BufferedConcurrentIterable(Iterator<E> source) {
this.source = source;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int i;
@Override
public boolean hasNext() {
synchronized (buffer) {
return i < buffer.size() || source.hasNext();
}
}
@Override
public E next() {
synchronized (buffer) {
if (i < buffer.size()) {
return buffer.get(i++);
}
E next = source.next();
buffer.add(next);
i++;
return next;
}
}
};
}
}
Regards, Peter
More information about the core-libs-dev
mailing list