Reputation: 4203
I am enhancing an existing algorithm that consists of multiple independent steps to use concurrent tasks. Each of the tasks will create multiple objects to hold its results. In the end, I would like to have a list of all the results to return from the controlling method. At the moment, my code looks something like that
private final ExecutorService pool = ...;
// A single task to be performed concurrently with other tasks.
private class WorkHorse implements Callable<Void> {
private final Collection<X> collect;
public WorkHorse(Collection<X> collect, ...) {
this.collect = collect;
}
public Void call() {
for (...) {
// do work
synchronized (this.collect) {
this.collect.add(result);
}
}
return null;
}
}
// Uses multiple concurrent tasks to compute its result list.
public Collection<X> getResults() {
// this list is supposed to hold the results
final Collection<X> collect = new LinkedList<X>();
final List<WorkHorse> tasks = Arrays.asList(
new WorkHorse(collect, ...), new WorkHorse(collect, ...), ...);
this.pool.invokeAll(tasks);
// ## A ##
synchronized (collect) {
return collect;
}
}
Do I actually need the synchronized
at "## A ##" to enforce a happens-before relationship with the modifying operations in the worker tasks? Or can I rely on all write operations to have happened after invokeAll
returns and be visible to the controlling thread? And is there any reason, why I should not return the results collection from within its own synchronized
block?
Upvotes: 4
Views: 3246
Reputation: 51153
You don't need the second synchronized
if you have the first one in there. As Zed notes, invokeAll()
will block until all tasks have completed. Meanwhile, the synchronization around add()
will ensure that the changes to the collection are visible to all threads, including the original calling thread.
As for whether you need the first one (which you didn't ask) -- I tried removing both synchronized
blocks and couldn't actually get it to fail, but having it in there is probably the safer bet. According to the javadoc for LinkedList
:
If multiple threads access a LinkedList concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally.
The other "2nd-generation" Collection
implementations have similar warnings.
Note by the way that there's nothing magic about synchronizing on the collection itself. You could declare a separate mutex (any old Object
would do) in the outer class, or synchronize on the outer class instance, and that would work just as well, so long as all WorkHorse
s synchronize on the same thing.
Upvotes: 0