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