I have a Java application which has worker threads to process jobs. A worker produces a result object, say something like:
class WorkerResult{
private final Set<ResultItems> items;
public Worker(Set<ResultItems> pItems){
items = pItems;
}
}
When the worker finishes, it does this operation:
...
final Set<ResultItems> items = new SomeNonThreadSafeSetImplSet<ResultItems>();
for(Item producedItem : ...){
items.add(item);
}
passToGatherThread(items);
The items
set is kind of a "unit-of-work" here. The passToGatherThread
method passes the items
set to a gather thread, of which only one exists at runtime.
Synchronization is not needed here, since race conditions cannot occur because only one thread (Gather-thread) reads the items
set. AFAICS, the Gather-thread may not see all items because the set is not thread-safe, right?
Assume I cannot make passToGatherThread
synchronized, say because it is a 3rd party library. What I basically fear is that the gather thread does not see all items because of caching, VM optimizations, etc. So here comes the question: How to pass the items set in a thread-safe manner, such that the Gather thread "sees" the proper set of items?
I have thought about (and discussed) this question a lot and I have come up with another answer, which, I hope, will be the best solution.
Passing a synchronized collection is not good in terms of efficiency, because each subsequent operation on that collection will be synchronized - if there are many operations, it may prove to be a handicap.
To the point: let's make some assumptions (which I do not agree with):
passToGatherThread
method is indeed unsafe, however improbable it seemspassToGatherThread
is called before the collection is filledThe simplest, cleanest and possibly the most efficient way to ensure that the collection passed to gatherer method is ready and complete is to put the collection push in a synchronized block, like this:
synchronized(items) {
passToGatherThread(items);
}
That way we ensure a memory synchronization and a valid happen-before sequence before the collection is passed, thus making sure that all objects are passed correctly.
You could simply use one of the thread-safe implementations of Set
that Java provides for your WorkerResult
. See for example:
Another option is to use Collections.synchronizedSet()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With