Ah, I see your point about that, and there’s the whole other possibility of working with an incompletely initialized class in general, something I don’t know all the rules about… another reason shared state is the devil’s playground I suppose.
when I need to initialize some static resource lazyly I do this, its easy and thread safe
public class X
{
private static class StaticStuff
{
public static MyObject lazyObject = new MyObject();
}
public void someMethod()
{
Object something = useResource(StaticStuff.lazyObject);
}
Maybe I’m on drugs (too?), but how could that possibly be enforced?
public class X {
public final int x, y;
public X() {
this.x = 5;
// expose instance, before constructor is complete, where final 'y' is still zero
Registry.register(this);
this.y = 6;
}
}
@Roquen - that’s what I thought too, hence my first reply. However, from further reading I think moogie may be right about the need for the reference to be marked volatile.
@Riven: well sure. A determine programmer can break about anything and in your example indeed the reference will be visible before the constructor has completed. But I’m sure we can agree that an object making itself visible in its constructor is very naughty. I was referring to:
x = new WhatEver();
The constructor is insured to be completed before the value of ‘x’ changes.
I have to admit I was not aware of the fact that it behaved differently in the ‘first’ Java Memory Model.
I thought it had to do with the order of bytecode operations. :persecutioncomplex:
ANEW some/Clazz // create instance
DUP
INVOKESPECIAL <init> // run constructor
ASTORE n // assign to local variable
Do I understand correctly that the above code would expose the instance to the local variable (line 4), before line 3 was fully executed? Possibly due to the JIT reordering the operations?
Like:
ANEW some/Clazz // create instance
ASTORE n // assign to local variable
ALOAD n // grab from local variable
INVOKESPECIAL <init> // run constructor
I think this is only the case where x is volatile (unless all accesses are in synchronized blocks).
Yes, though I think your example is wrong in the sense that we’ve got to be talking about non-local variables? The variable has to be visible from two different threads - it was a memory model change in the sense of when operations on one thread are visible to another.
And unfortunately, and very occasionally, necessary. I’ve sometimes wondered about the ability to have two-stage construction here actually, where all assignments (particularly final ones) happen in a first stage where ‘this’ is invalid (including all subclass constructors), then an optional second stage for registering ‘this’ with other things, etc. Probably a bad idea!
@Riven: exactly that thought, but as noted put/get field/static instead. I want to think that it was always illegal from the verifiers standpoint, but not a mandated illegal transform (but heck I might be misremembering).
WRT: volatile. Err, I don’t think that volatile changes anything in this case.
Why’s that? private constructor, externally visible creation method which calls constructor, makes the reference and return the new instance…done. That seems to cover all bases.
Volatile just guarantees that operations on it are atomic, and that all threads will eventually see changes to it. IIRC it does not give any guarantees about order. You must use locks for that.
In fact if we are talking about access to a variable across threads, then well shouldn’t locks be used anyway?
Read the article I posted above, or at least the bit about the new memory model near the bottom under Fixing Double-Checked Locking using Volatile - http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html Assuming the list of people signing that paper is accurate, that seems pretty definitive!
Seems not to be required if the object being created is immutable with all final fields though.
Actually, that’s what I do most of the time. I think the situation I’m thinking of was with an abstract superclass constructor, but even then I think you’re right - there are better ways! And it’s OT.
My understanding with the new memory model is that single thread access to a volatile is strictly in order, but no guarantees between (under the old it gave you atomic operations). That was not my understanding of memory barriers. Also i only ever properly learnt the old memory model (some company wanted me java certified!)
But then again… its been a while since i did any of this stuff outside Java. And these days with java.util.concurrent.*, there is not much more I need.
Then I can point you to any number of references that volatile has an affect on this (stopping out-of-order instructions being visible to another thread), as does whether the fields of the constructed object are final or not. Strengthening the semantics of volatile and final would seem to be a major part of the new memory model.
Absolutely, but as a purely theoretical concurrency masturbation thread this is quite interesting!
@delt0r: yes indeed. I can’t recall the last time I wrote synchronized nor volatile.
@nsigma: In the sense that (assuming I’m not wrong) that volatile is a memory barrier, then yeah it changes things…but what I’m attempting to say is that ‘x’ doesn’t need to be volatile to insure correctness (again that x is only set after the constructor is complete).
Not sure if we’re really going around in circles saying almost the same thing here?!
What I mean is, as far as I understand it.
x may be assigned before the constructor is complete.
x doesn’t need to be volatile to ensure correctness in a single thread. This is not the same as saying the value of x is not set before the constructor is completed - instructions may still be out of order but correct with regards to the creating thread.
Without the use of volatile another thread can see x in a half-initialized state (unless all x’s fields are final). Therefore volatile ensures that the constructor is really complete prior to assignment.
Unless I’m incorrect: no to points 1 & 3. x may not be assigned to a field prior to completion of the constructor. The only issue about x being volatile (other than memory barrier) is that if another thread previously read x before the assignment and re-examines it after, then it’s not insured to have the up-to-date value as it’s not require to re-read on each access.
The section on initialization safety specifically talks only about final fields.
The section on volatile / double-checked locking specifically talks about how the semantics of volatile were expanded to non-volatile fields within the constructed object, and how assignment of fields within the constructor can be reordered with respect to the assignment of the object itself.