From: Peter Duniho on
markspace wrote:
> Lew wrote:
>
>> markspace wrote:
>>> I agree that the only practical way to pass an object to the EDT
>>> involves a shared lock. However, invokeLater() makes no such guarantee,
>>
>> Are you kidding? The whole documented point of 'invokeLater()' and
>> 'invokeAndWait()' is to push things to the EDT in a thread-safe manner.
>
>
> Addendum: no, I'm not kidding. Where do you see it documented that the
> purpose of invokeLater() is to "push things" to the EDT? I only see it
> documented that invokeLater() executes code, so that already existing
> objects, created on the EDT, can be accessed in a thread safe manner.
>
> All the examples of using invokeLater() I can find carefully avoid any
> access of objects created off the EDT, unless that object is made thread
> safe some other way (e.g., use of final keyword).

In general, the examples I've seen use "final" only because that's the
only way to refer to a local variable in an anonymous class. It has
nothing to do with "carefully avoiding any access of objects created off
the EDT".

The example that IMHO is _definitive_ would be the discussion found here:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

And there's no example of any code there where any specific attempt is
made to synchronize access to data initialized for use in the Runnable.
Here's an excerpt that specifically _doesn't_ do synchronization:

void printTextField() throws Exception {
final String[] myStrings =
new String[2];

Runnable getTextFieldText =
new Runnable() {
public void run() {
myStrings[0] =
textField0.getText();
myStrings[1] =
textField1.getText();
}
};
SwingUtilities.invokeAndWait
(getTextFieldText);

System.out.println(myStrings[0]
+ " " + myStrings[1]);
}

Now, the above is actually (according to you) "broken" in the other
direction. That is, data is initialized in the EDT, and then used after
the invokeAndWait() method returns, but no explicit synchronization is
present to ensure that the data is visible in the original thread. But
it's exactly the same issue you're talking about. If it's broken going
from the original thread to the EDT (as you claim), it's also broken
going from the EDT to the original thread.

> If you can show documentation that even partially supports your claim,
> I'd love to see it.

If you can show documentation that even partially supports your claim,
I'd love to see it.

You'd think that if it were important to explicitly synchronize data
initialized prior to calling one of the invoke…() methods when that data
will be used in the Runnable, the documentation would actually mention
that fact.

The documentation that supports my claim (and Lew's, which is identical)
is the JLS, where it describes the "happens-before" guarantees that are
related to the use of the various synchronization techniques. Along
with, of course, the documentation for the invoke…() methods, which
promise that those methods will actually work.

It would be nice if the JDK docs themselves distilled the admittedly
more complicated presentation found in the JLS, to make it more clear to
those less familiar with the JLS and concurrency issues in general. But
the fact that it doesn't isn't proof of the invoke…() methods being broken.

Regardless of the state of the documentation, as I have pointed out a
number of times already, it is simply not possible for the invoke…()
methods to work correctly and yet not ensure the necessary order of
operations:

• The invoke…() methods both take as an argument a reference to a
Runnable instance

• This reference _must_ be passed to the EDT somehow (after all,
that's where the Runnable's run() method is going to be called), and the
only way to achieve that in a thread-safe way is to synchronize some
shared data between the two threads

• The act of synchronizing shared data, even if simply by use of
"volatile", _guarantees_ that any data written before the synchronized,
shared data is written will be visible to any other thread that reads
the data after the shared data is read

Thus, everything that your non-EDT thread that calls the invoke…()
method writes before that call is visible in the EDT from the moment the
Runnable's run() method starts executing (actually, a bit before that,
when the Runnable's reference was actually read, but there's no
practical way to take advantage of that…for all intents and purposes,
it's the stuff in and after the run() method that counts).

There is simply no other way that it can work. The entire invoke…()
paradigm would be fundamentally broken otherwise.

Pete