Re: how to make this code thread safer?
markspace wrote:
[...]
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.
I agree that the only practical way to pass an object to the EDT
involves a shared lock. However, invokeLater() makes no such guarantee,
and they'd be within their rights to change their implementation of
invokeLater() and remove any shared locks, thus removing any
synchronization and any happens-before semantics.
I disagree. Java concurrency and especially that related to the EDT
(that is, in light of the fact that EDT-related issues are almost always
about concurrency) is poorly documented, I will grant.
But it is simply not possible for the invoke methods to not provide the
necessary synchronization. Whatever has happened in one thread prior to
the call to an invoke method _must_ be visible to the code executing as
a result of that call to the invoke method. The invoke methods would be
useless otherwise.
Contrast this with
the docs of some of the concurrent classes like Executor, which does
provide an explicit memory consistency guarantee.
Thus, I think the minimum you can get away with is a volatile field:
public class Main {
public static void main( String... args ) {
final MyClass test = new MyClass();
SwingUtilities.invokeLater( new Runnable() {
private volatile MyClass temp = test;
public void run() {
MyClass model = temp;
... // ok to use model now...
}
} );
}
}
If what you say were true, then you could not even count on the Runnable
instance that you created to be valid during the invoked execution,
since that's something that happened before the call to invokeLater(),
but which wasn't synchronized. The "temp" field itself would be safe,
but nothing else in the Runnable instance would be (including, for
example, a v-table as might be used to dispatch the call to run()).
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.
This is an even scarier assertion, imo. I don't agree that we should
rely on what's "likely" to happen in concurrency, unless you only want
it "likely" that your program will run correctly.
You are misreading my statement. It's not about relying on "what's
likely". It's that it is "likely" there is a synchronization mechanism
in place, and if so one can rely on it.
You can certainly move data from one thread to another unsafely. For
example, through use of a non-volatile field. And yes, even if the data
managed to get from one thread to the other via that field, there would
be no guarantees about other data that may have been updated in one
thread before the data in question, being visible in the other thread
even though the data in question is visible.
But, assuming the data itself was moved safely (e.g. through use of a
volatile field), then all of the things that happened in one thread
before that data was moved from that thread will then be visible in
another thread after the data is moved to that thread. And since for
the code to be thread-safe, the data _must_ be moved safely, it is
"likely" that the data was moved safely (inasmuch as one hopes it is
"likely" that the code is thread-safe).
The likeliness depends of course on how much you trust the person who
wrote the code moving the data. But for mechanisms found in the JDK, I
find the level of likelihood quite high. For a random Java programmer I
stumble across on the street, not so much.
Pete