I know that you can safely publish a non-thread safe object by writing the reference to a final
or volatile
field which will later be read by exactly one other thread, provided that upon publication, the thread that created the object discards the reference to it so it can no longer interfere with or unsafely observe the object's use in the other thread.
But in this example, there's no explicit final
field, only final
local variables. If the caller discards its reference to unsafe
, is this safe publication?
void publish(final Unsafe unsafe) {
mExecutor.execute(new Runnable() {
public void run() {
// do something with unsafe
}
}
}
I found a few Q&As, like this one, suggesting that final
local variables are implicitly "copied" into anonymous classes. Does that mean that the above example is equivalent to this?
void publish(final Unsafe unsafe) {
mExecutor.execute(new Runnable() {
final Unsafe mUnsafe = unsafe;
public void run() {
// do something with mUnsafe
}
}
}
Edit for clarification:
Unsafe
could be anything, but say it's something like this:
public class Unsafe {
public int x;
}
And mExecutor
is anything that satisfies the contract of Executor
.
Safe publication makes all the values written before the publication visible to all readers that observed the published object. It is a great simplification over the JMM rules of engagement with regards to actions, orders and such.
Final variables are immutable references, so a variable declared final is safe to access from multiple threads. You can only read the variable, not write it.
1) Immutable objects are by default thread-safe because their state can not be modified once created. Since String is immutable in Java, it's inherently thread-safe. 2) Read-only or final variables in Java are also thread-safe in Java.
A final field is one that cannot be changed once it is initialized.
Although, admittedly, I'm not entirely sure that I got the actual point of your question, and (as pointed out in the comments) the issue is likely not really an issue in your particular case, maybe the relevant insights can be gained from a test/example
Considering the following class:
import java.util.concurrent.ExecutorService;
class Unsafe
{
}
class SafePublication
{
private final ExecutorService mExecutor = null;
public void publish(final Unsafe unsafe)
{
mExecutor.execute(new Runnable()
{
@Override
public void run()
{
// do something with unsafe
System.out.println(unsafe);
}
});
}
}
One can compile it, and obtain two .class
files:
SafePublication.class
SafePublication$1.class
for the inner classDecompiling the class file for the inner class yields the following:
class SafePublication$1 implements java.lang.Runnable {
final Unsafe val$unsafe;
final SafePublication this$0;
SafePublication$1(SafePublication, Unsafe);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LSafePublication;
5: aload_0
6: aload_2
7: putfield #2 // Field val$unsafe:LUnsafe;
10: aload_0
11: invokespecial #3 // Method java/lang/Object."<init>":()V
14: return
public void run();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #2 // Field val$unsafe:LUnsafe;
7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
}
One can see that for the final
parameter, there is indeed a field introduced in this class. This field is val$unsafe
, and it is a final field in the class file sense, and it is initialized in the constructor.
(This is not entirely equivalent to the second code snippet that you posted, because the second one contains two final fields, and they are both initialized with the same value. But regarding the issue of safe publication, the effect should be the same).
You left some important code out of your first example: The mExecutor
object probably owns a BlockingQueue
. The mExecutor.execute(r)
call probably calls q.put(r)
to add your task to the queue, and then sometime later, the worker thread calls r=q.take()
to obtain the task before it can call r.run()
.
The put()
and take()
methods of the blocking queue will establish the same kind of "happens before" relationship between events in the two threads that would be established by one of the "safe publication" idioms.
Whatever the first thread updates in memory before calling q.put(r)
is guaranteed to become visible to the second before the q.take()
call returns.
The question seems to be partly answered by this answer:
Java multi-threading & Safe Publication
at least regarding "safe publication".
Now if the caller discards its reference the variable will be safe, because there exist no reference to the variable except in the final local variable.
And regarding the code examples - in my eyes both code snippets are equivalent. Introducing an additional local variable doesn't change the semantics, in both cases the compiler recognizes the reference as immutable and let you go with it.
EDIT - I am leaving this part to document my misinterpretation of the OP's question
To clarify - I take usage of final
or volatile
as granted in this example, so the proper memory barrier catering for the visibility of the object reference is there, the only point is the possible mutability of the non thread-safe object which cannot be guaranteed using memory barriers and actually has nothing to do with them. It can be taken care of either through proper synchronization or leaving only one reference to the content.
EDIT2 – after reading the OP’s comments
I have just looked at the JSR 133 FAQ - AFAIU a safe publication of a reference to object using a memory barrier doesn't guarantee that the unsychronized fields of the mentioned referenced object are visible too. Neither by final
nor volatile
.
If I am not misinterpreting this FAQ only synchronizing on the same monitor defines a “happens-before” relationship for all the writes one thread did before releasing the synchronizing lock and acquiring the lock on the same monitor by another thread.
I may be mistaken but it sounds to me as if also non-synchronized fields of the referenced object would be visible.
If using final
keyword (like in your example where the parameter gets inserted as a final
field) - only instance fields of the referenced object that themselves are final
are guaranteed to be visible after the construction of the object ends.
But in the BlockingQueue
(and as its implementation the LinkedBlockingQueue
) I see no synchronized
keyword whatsoever – it seems that it uses some very clever code to implement synchronization by using volatile
fields, to me it doesn’t sound like a synchronization on a monitor in the sense described in JSR 133.
Which would mean that common blocking queues used by Executor do not guarantee visibility of non-final fields of your Unsafe
instances. While the reference itself can be safely published using just the final
keyword, the safe publishing of the fields this reference points to requires either the fields to be final
, too, or a synchronization with a monitor shared by the writer and reader.
Don’t shoot the messenger :-).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With