Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java thread-safe passing of collection objects from one thread to another

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?

like image 749
AlexLiesenfeld Avatar asked Feb 11 '13 12:02

AlexLiesenfeld


2 Answers

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):

  • the mentioned passToGatherThread method is indeed unsafe, however improbable it seems
  • compiler can reorder the events in the code so that the passToGatherThread is called before the collection is filled

The 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.

like image 147
Dariusz Avatar answered Nov 16 '22 06:11

Dariusz


You could simply use one of the thread-safe implementations of Set that Java provides for your WorkerResult. See for example:

  • CopyOnWriteArraySet
  • ConcurrentSkipListSet

Another option is to use Collections.synchronizedSet().

like image 1
joergl Avatar answered Nov 16 '22 06:11

joergl