Assuming this class:
public class AmIThreadSafe {
private int a;
private int b;
AmIThreadSafe(int a, int b) {
this.a = a;
this.b = b;
}
}
Assuming that instance's reference to this class (declared as volatile) is accessible by some threads (leading to race condition) as soon as the this(reference) escapes:
volatile AmIThreadSafe instance = new AmIThreadSafe(1,2);
Here, I'm sure that the fact of assigning instance reference happens-before reading by threads.
But what about the AmIThreadSafe's fields?
Does the external volatile keyword also imply an happens-before relation concerning a and b fields?
Or is it rather possible to end up with any thread seeing stale values (default values 0 in this case since int) due to a potential statements reordering during constructor?
In other word, should I declare a and b final or volatile to prevent any surprises with the JMM or is just indicating volatile on the instance's reference enough?
----------------UPDATED POST - A GOOD ANSWER:----------------------------
The following article confirms by its sample that in my case, a and b are protected from JMM optimisations that prevent a permanent happens-before relation.
http://jeremymanson.blogspot.fr/2008/11/what-volatile-means-in-java.html
Declaring instance as volatile does not make its fields volatile, but if I understand your question correctly, then — yes, it's enough in your case.
Per §17.4.5 of the spec:
- a
volatilewrite in one thread happens-before any subsequentvolatileread in another thread. - statements within the same thread have the happens-before relationship that you'd expect.
- happens-before relationships are transitive.
So, if a thread perceives instance as having been initialized, then the initialization of instance happened-before it, and the initialization of instance's fields happened-before that, so the thread will perceive instance's fields as having been initialized.
No it's not enough to make it volatile. Thread safety depends on the usage though. E.g., this could still produce unexpected results if another thread is modifying the values.
assuming public variables for simplicity
volatile AmIThreadSafe instance = new AmIThreadSafe(1,2);
if (instance.x == 0) {
// instance.x might have changed between checking and assigning
instance.x = instance.x + 1;
}
volatile only applies to a variable (e.g., x and y are not automatically volatile just because instance is). This should be clear from JLS 8.3.1.4
The volatile in your case applies only to the reference of AmlThreadSafe. You still have to make the instance variables (a and b) volatile or access them in a synchronized block. Otherwise you can get stale data.
Yes.
thread 1 thread 2
1 write(a)
2 write(instance)
3 read(instance)
4 read(a)
Since instance is volatile, [2] happens-before [3].
Since happens-before is transitive, we have hb(1,2), hb(2,3), hb(3,4), therefore hb(1,4)
If a and b are only modified in the constructor, then in this case you should be fine because the object is created (and a and b set) BEFORE the reference is assigned to instance, and any other threads won't have locally-cached copies of the memory at those locations because it's a new object that the thread couldn't have seen before. In other words, I don't believe it's possible that another thread will ever be able to see the "default" value of 0 for a and b because the constructor will run completely before the object's reference is assigned to instance.
However if a and b can be modified after the constructor, then the other answers here are correct - you need synchronization around them.
If you are going to assume that a and b won't be modified outside of the constructor, then there's no reason not to make them final anyway, just to be safe.
Example :
class Something{
private volatile static Something instance = null;
private int x;
private int y;
private Something(){
this.x = 1;
this.y = 2;
}
public static Something getInstance() {
if (instance == null) {
synchronized (Something.class) {
if (instance == null)
instance = new Something();
}
}
}
return instance;
}
}
Explanation :
Lets say we have the above Code:
Now lets assume instance is not volatile for some time:
Thread#1 :
Comes in invoke getInstance method, Check for instance value{since null}, will go inside the IF condition, Access the Lock now again finds that instance == null, calls Something constructor. Now goes inside Constructor body.
As soon as Thread#1 goes inside Constructor body, Context Switch happens and now Thread #2 gets the turn to execute.
Thread#2 :
Invokes get Instance, But suddenly finds that instance is not null?Why{Reason will discuss just after this}and hence assign partially constructed Object to reference and returns it.
Now Situation is like this : Thread#1 Still needs to construct the Object Completely{needs to completely construct it} and Thread#2 got reference for partially constructed Object, and if it uses it like say reference.x //will print "x" Default Value and not "1"
Why partially constructed Object reference is returned in case Of Thread#2 ? Reason is simple: Statements Reordering. The Steps are simple for Object creation and reference association :
- Allocate Memory in Heap.
- Execute the Body of Constructor which will initialize class members.
- Once above Step is completed, Reference to newly created Object.
But Sometimes Compiler can execute these Instructions out Of order, which means :
It might happen something like this :
- Allocate Memory in Heap.
- Reference to newly created Object.
- Execute the Body of Constructor which will initialize class members.
Once the above two Steps happens and if Context Switching Happens, then reference will point to Object which is not initialized or it might be the case that Inside Constructor Body Context Switch happens then in that case reference will refer to partially initialized Object.
If such scenario occurs then reference will neither be null nor complete and hence it will break our Singleton motivation.
Now How Volatile will save our Life from such embarrassments :
As we know Volatile work with two principles : 1)Visibility 2)Happens Before Relationship.
Now Happens Before Relationship comes into Picture here.
So reference is volatile Write, so all statements should happen before any Volatile write.Again If we look at our Object construction steps:
- Allocate memory for Object
- Initialize member variables{Constructor Body}
- Assign Object reference to volatile variable instance.
Step 3 has Volatile Variable write and as per Happens before .. All statements write guaranteed to be made available to step 3. And since it is volatile therefore No reordering will happen between volatile and non volatile statements, which was not the case in Old Java Memory Model.
So before executing Step 3, Step 1 and Step 2 are guaranteed to be happened and made available to Step 3. {In which order Step 1 and step 2 occurs we don't bother about it.}
So from this Thread will see either Completely created Object or null
来源:https://stackoverflow.com/questions/13371066/volatile-for-reference-type-does-it-always-avoid-publication-of-references-iss