Re: how to make this code thread safer?
markspace wrote:
www wrote:
The code below is completely fine if running on single-thread
environment. But it could break if running on multi-thread environment.
public class MyClass
{
private Person tim = new Person("Tim");
private Person tom = new Person("Tom");
Here's a weird question for this list at large. Are the above lines
even thread safe? Objects are guaranteed to be correctly constructed,
but these objects might be mutable. And the fields tim and tom are not
final, so they aren't guaranteed to be published correctly either.
So one thread has to construct this "MyClass" and then some other thread
might consume it (note that the methods of MyClass are not static, so an
instance does have to be constructed):
public class Main {
public static void main( String... args ) {
MyClass test = new MyClass();
... // now what?
}
}
So here if one then passed the "test" object to, for example, the EDT
for use in a GUI, one still needs synchronization/happens-before to make
the "test" object correctly visible to the EDT, or any other thread
besides main, yes?
For the EDT example, there are actually two different mechanisms that
should ensure thread safety. One of them, I would have to look up to
make sure is supported in the JVM (but I'm pretty sure it is). The
other is inherent in your statement "passed the 'test' object to?the EDT".
The first mechanism has to do with semantics surrounding object
construction. In .NET, the specification ensures that whatever happens
in the constructor "happens before" any other code that uses the
reference after the constructor completes (.NET doesn't actually use the
term "happens-before", but it's the same idea). I would expect Java to
be the same in this respect.
Note that this is a good reason for not letting the "this" reference
leak out of the constructor, because doing so would circumvent whatever
assurances the object construction semantics would normally provide.
The second mechanism has to do with how object references get from one
thread to another. In particular, keep in mind that "happens
before"/"happens after" has a sort of transitive nature to it.
Everything that happens before something synchronized in one thread is
visible to everything that happens after the inspection of that
synchronized something in a different thread.
In the case of the EDT, the typical way an object reference would get
from another thread to the EDT is via the invokeLater() or
invokeAndWait() methods. These methods are inherently synchronized
themselves; it's a natural consequence of what must happen for the data
passed to the methods to get to the EDT.
This means that everything that happened in that other thread before the
call to invokeLater() or invokeAndWait() is necessarily going to be
visible to the EDT, as long as it only inspects that data as a result of
whatever Runnable was passed to the invoke?() method. Likewise if the
data was passed not via an invoke?() method but rather via, for example,
a synchronized queue monitored by a Swing timer that inspects it
periodically. All bets are off, of course, if the data is transferred
via some unsynchronized means.
This applies more generally to any cross-thread communication mechanism.
Even if not using the EDT, there is likely some synchronized
data-transfer mechanism that acts as the go-between, allowing that newly
constructed reference to safely move from one thread to the other. So
long as the reference is not inserted into that synchronized mechanism
until the constructor returns, then the other thread has the full
guarantee of the object being fully initialized that the original thread
where the object was created has.
In other words, even if it turns out that I'm mistaken and the JVM
doesn't provide any specific synchronization guarantees with respect to
object construction, code that is otherwise thread-safe anyway will
automatically ensure that the initialization of the object is itself
thread-safe.
Note also that this second mechanism applies not just to construction,
but to _any_ mutation that may occur in one thread prior to the object
being delivered to another thread.
Finally, part of your question brings up the possibility of the "Person"
object being mutable. And well, yes. If those classes are mutable,
there could yet still be a thread-safety problem if they mutate
post-construction without synchronization being used between threads
handling those objects. But that's not related to the question of the
initialization of the object. That would be just the usual "synchronize
mutations" problem that generally comes up with concurrent code.
Pete