My question is related to this question, which already has an answer:
yes, there is a happens-before relationship imposed between actions of the thread calling
invokeLater
/invokeAndWait
and actions on the EDT of the runnable thereby submitted.
My question is a bit more general: Is it even possible to implement a method, such as invokeAndWait
, such that it works properly, but not imposing a happens-before relationship? By the method working properly I mean the following:
- The submitted
Runnable
is guaranteed to be executed exactly once. - The submitted
Runnable
is executed on a specific thread. - The method waits until the execution of the submitted
Runnable
has finished. - The method is guaranteed to return after the execution of the submitted
Runnable
has finished.
To me there seems to be no way to implement this without imposing a happens-before relationship, or am I wrong? If so, please include an example implementation, which proves this.
The most difficult requirement here is:
Using a non-
volatile
(Plain) field for transfering the work task from the submitter to the executor would not create a happens-before relationship, but would also not guarantee that the executor sees the task at all or in a finite amount of time. The compiler would be able to optimize away assignments to that field, or during runtime the executor thread might only read the value from its cache instead of from main memory.So for code using Java 8 or lower, I would say the answer is "No, such an
invokeAndWait
method is not possible" (except maybe using native code).However, Java 9 added the memory mode Opaque. The page "Using JDK 9 Memory Order Modes" by Doug Lea, the author of JEP 193 (which added this functionality), describes this in great detail. Most importantly Opaque mode is weaker than
volatile
but provides still the following guarantee:When designing such an
invokeAndWait
method without happens-before relationship you also have to consider that an action before starting a thread happens-before the first action in that thread (JLS §17.4.4). So the worker thread must be started before the action is constructed.Additionally the "
final
field semantics" (JLS §17.15.1) have to be considered. When the caller ofinvokeAndWait
creates theRunnable
in the form of a lambda expression, then the capturing of variables by that lambda has (to my understanding) implicitfinal
field semantics.Proving or disproving thread-safety or happens-before relationships using examples is difficult, if not impossible, due to being hardware and timing dependent. However, tools like jcstress can help with this.
Below is a (simplified) potential implementation for an
invokeAndWait
without happens-before relationship. Note that I am not completely familiar with the Java Memory Model so there might be errors in the code.