views:

135

answers:

3

Assuming that I have the following code:

final Catalog catalog = createCatalog();

for (int i = 0; i< 100; i++{
    new Thread(new CatalogWorker(catalog)).start();
}

"Catalog" is an object structure, and the method createCatalog() and the "Catalog" object structure has not been written with concurrency in mind. There are several non-final, non-volatile references within the product catalog, there may even be mutable state (but that's going to have to be handled)

The way I understand the memory model, this code is not thread-safe. Is there any simple way to make it safe ? (The generalized version of this problem is really about single-threaded construction of shared structures that are created before the threads explode into action)

+1  A: 

You need to synchronize every method that changes the state of Catalog to make it thread-safe.

public synchronized <return type> method(<parameter list>){
...
}
Ahmad
Liberal application of 'synchronized' can lead to painful deadlocks if you're not careful.
Kevin Montrose
+1  A: 

Assuming you handle the "non-final, non-volatile references [and] mutable state" (presumably by not actually mutating anything while these threads are running) then I believe this is thread-safe. From the JSR-133 FAQ:

When one action happens before another, the first is guaranteed to be ordered before and visible to the second. The rules of this ordering are as follows:

  • Each action in a thread happens before every action in that thread that comes later in the program's order.
  • An unlock on a monitor happens before every subsequent lock on that same monitor.
  • A write to a volatile field happens before every subsequent read of that same volatile.
  • A call to start() on a thread happens before any actions in the started thread.
  • All actions in a thread happen before any other thread successfully returns from a join() on that thread.

Since the threads are started after the call to createCatalog, the result of createCatalog should be visible to those threads without any problems. It's only changes to the Catalog objects that occur after start() is called on the thread that would cause trouble.

Laurence Gonsalves
Ahem, it seems like your initial assumption is really redefining my question to be something else ? I *know* I will have to handle mutable state, but I was hoping there was a way to avoid handling all the non-final, non-volatile stuff.
krosenvold
You said "but that's going to have to be handled", implying that you knew that part had to be dealt with. You then said "The generalized version of this problem is really about single-threaded construction of shared structures that are created before the threads explode into action", which is what I was answering: creating shared structures in a single thread before the other threads that use them have started is fine. If the other threads had been started before the construction of these objects then there'd be a problem, and you'd need some other memory barrier to ensure consistency.
Laurence Gonsalves
+4  A: 

No, there's no simple way to make it safe. Concurrent use of mutable data types is always tricky. In some situations, making each operation on Catalog synchronized (preferably on a privately-held lock) may work, but usually you'll find that a thread actually wants to perform multiple operations without risking any other threads messing around with things.

Just synchronizing every access to variables should be enough to make the Java memory model problem less relevant - you would always see the most recent values, for example - but the bigger problem itself is still significant.

Any immutable state in Catalog should be fine already: there's a "happens-before" between the construction of the Catalog and the new thread being started. From section 17.4.5 of the spec:

A call to start() on a thread happens-before any actions in the started thread.

(And the construction finishing happens before the call to start(), so the construction happens before any actions in the started thread.)

Jon Skeet
Hmm, interesting: Are you saying I'm safe if I synchronize all updates to mutable state even if there exists state that is only "effectively immutable"? (I *know* this is a risky model from a maintenance perspective)
krosenvold
Well, I wouldn't say that makes you safe in general - but it makes you safe in pure memory model terms. Note that you need to synchronize all *accesses* not just *updates* - otherwise you could get "stale" data when you read it.
Jon Skeet
Yup. Good answer ;)
krosenvold