Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe publication of local final references

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.

like image 639
Kevin Krumwiede Avatar asked Jul 07 '16 17:07

Kevin Krumwiede


People also ask

What is safe publication?

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.

Are final fields thread safe?

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.

Is final thread safe java?

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.

When should a field be final?

A final field is one that cannot be changed once it is initialized.


3 Answers

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 class

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

like image 57
Marco13 Avatar answered Oct 06 '22 06:10

Marco13


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.

like image 24
Solomon Slow Avatar answered Oct 06 '22 07:10

Solomon Slow


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

like image 20
Tomasz Stanczak Avatar answered Oct 06 '22 07:10

Tomasz Stanczak