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