Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Caching of non-volatile variables by different threads

The situation is the following:

  1. I have an object with lots of setters and getters.
  2. Instance of this object is created in a one particular thread where all values are set. Initially I create an "empty" object using new statement and only then I call some setters methods based on some complicated legacy logic.
  3. Only then this object became available to all other threads that use only getters.

The question: Do I have to make all variables of this class volatile or not?

Concerns:

  • Creation of a new instance of the object and setting all its values is separated in time.
  • But all other threads have no idea about this new instance until all values are set. So other threads shall not have a cache of not fully initialized object. Isn't it?

Note: I am aware about builder pattern, but I cannot apply it there for several other reasons :(

EDITED: As I feel two answers from Mathias and axtavt do not match very well, I would like to add an example:

Let's say we have a foo class:

class Foo {   
    public int x=0;   
}

and two threads are using it as described above:

 // Thread 1  init the value:   
 Foo f = new Foo();     
 f.x = 5;     
 values.add(f); // Publication via thread-safe collection like Vector or Collections.synchronizedList(new ArrayList(...)) or ConcurrentHashMap?. 

// Thread 2
if (values.size()>0){        
   System.out.println(values.get(0).x); // always 5 ?
}

As I understood Mathias, it can print out 0 on some JVM according to JLS. As I understood axtavt it will always print 5.

What is your opinion?

-- Regards, Dmitriy

like image 778
Dime Avatar asked Jul 12 '11 08:07

Dime


People also ask

How can we ensure that all the threads read the updated value of any variable?

Put the assignment in a synchronized block.

How volatile will work if we have multiple threads writing to a volatile variable?

If you write volatile variable from multiple threads without using any synchronized constructs, you are bound to get data inconsistency errors. Use volatile variables without synchronization in case of single write thread and multiple read threads for atomic operations.

What is thread cache in Java?

In Java, threads doesn't cache any object or variable, they just have a reference to an instance of an object. Talking about thread cache memory is more like talking about operative systems threads...


2 Answers

In this case you need to use safe publication idioms when making your object available to other threads, namely (from Java Concurrency in Practice):

  • Initializing an object reference from a static initializer;
  • Storing a reference to it into a volatile field or AtomicReference;
  • Storing a reference to it into a final field of a properly constructed object; or
  • Storing a reference to it into a field that is properly guarded by a lock.

If you use safe publication, you don't need to declare fields volatile.

However, if you don't use it, declaring fields volatile (theoretically) won't help, because memory barriers incurred by volatile are one-side: volatile write can be reordered with non-volatile actions after it.

So, volatile ensures correctness in the following case:

class Foo {
    public int x;
}
volatile Foo foo;

// Thread 1
Foo f = new Foo();
f.x = 42;
foo = f; // Safe publication via volatile reference

// Thread 2
if (foo != null)
     System.out.println(foo.x); // Guaranteed to see 42

but don't work in this case:

class Foo {
    public volatile int x;
}
Foo foo;

// Thread 1
Foo f = new Foo();
// Volatile doesn't prevent reordering of the following actions!!!
f.x = 42;
foo = f;

// Thread 2
if (foo != null)
     System.out.println(foo.x); // NOT guaranteed to see 42, 
                                // since f.x = 42 can happen after foo = f

From the theoretical point of view, in the first sample there is a transitive happens-before relationship

f.x = 42 happens before foo = f happens before read of foo.x 

In the second example f.x = 42 and read of foo.x are not linked by happens-before relationship, therefore they can be executed in any order.

like image 63
axtavt Avatar answered Nov 15 '22 16:11

axtavt


You do not need to declare you field volatile of its value is set before the start method is called on the threads that read the field.

The reason is that in that case the setting is in a happens-before relation (as defined in the Java Language Specification) with the read in the other thread.

The relevant rules from the JLS are:

  • Each action in a thread happens-before every action in that thread that comes later in the program's order
  • A call to start on a thread happens-before any action in the started thread.

However, if you start the other threads before setting the field, then you must declare the field volatile. The JLS does not allow you to assume that the thread will not cache the value before it reads it for the first time, even if that may be the case on a particular version of the JVM.

like image 26
Mathias Schwarz Avatar answered Nov 15 '22 15:11

Mathias Schwarz