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
.
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).