From: Peter Duniho on
markspace wrote:
> Peter Duniho wrote:
>
>> It is worth noting that the reference to the object itself need not be
>> the thing that is subject to synchronization. Anything that
>> introduces the necessary memory barrier is sufficient.
>
> One caveat here: "anything" must be the same thing in all threads which
> access the shared data in question. If thread A locks objectA, and
> thread B locks objectB, they are in no way synchronized, nor is there
> any happens-before relationship establish for any shared data they access.

Right.

>> So, for example, any of the "storing a reference to it" examples above
>> are actually satisfied by any data being stored in the described
>> location, so long as the data being published has been initialized
>> prior to storage _and_ when the data is later read by a different
>> thread, that thread first retrieves whatever data was in fact stored
>> in the described location.
>
> I don't think this is correct for all of the examples Joshua listed. For
> example, storing a reference to a field that is properly guarded by a
> lock does NOT create a happens-before relationship for "any data" later
> read by a different thread.

Yes, it does. As long as the different thread reads the synchronized
data first (which is what I wrote).

> Actually, the more I reread your statement, the more I'm sure I have no
> idea what the heck you are actually trying to say. I can't think of any
> combination of events where the events you listed actually bear on each
> other. Could you produce an example where that's relevant?

class Test
{
private volatile boolean set = false;
private int data = 0;

void methodA()
{
data = 5;
set = true;
}

void methodB()
{
if (set)
{
System.out.print(data);
}
}
}

In the above example, if methodA() is executed on one thread, and then
methodB() is executed in a different thread, methodB() is guaranteed to
see the new value for "data".

Note that I didn't need to store the reference to the Test object
anywhere in order to ensure synchronization. I didn't need to store a
reference to _anything_, for that matter. It suffices to introduce the
partial memory barrier provided by the volatile write and read. All
writes that occur before the volatile write to a variable are guaranteed
to be visible in any other thread after it performs the volatile read
from the same variable.

Pete
From: Peter Duniho on
markspace wrote:
> Joshua Cranmer wrote:
>
>> Immutability is the easiest way to guarantee safe publication: final
>> fields guarantee that they can be used by any thread safely.
>
> Note quite. Final fields by themselves don't guarantee immutability or
> safe publication. An immutable object must:
>
> * All fields are final.
> * It is properly constructed. (No "this escapes." That bit is important.)
> * Its state cannot be modified after construction.
>
> Those three things must hold for an object to be treated as immutable by
> the JVM. Mess up any one, and poof, no more immutability.

I would be interested in seeing an example where your third point is
violated but the first point is not.
From: markspace on
Peter Duniho wrote:

> class Test
> {
> private volatile boolean set = false;

> In the above example, if methodA() is executed on one thread, and then
> methodB() is executed in a different thread, methodB() is guaranteed to
> see the new value for "data".


Yes, for volatile, this works. Volatile fields have special semantics
which prevent re-ordering reads and writes by the compiler, so this is
guaranteed to work.

I said it didn't work for all of the cases Joshua listed, particularily
the synchronized one. Replace the volatile with a synchronized block
and it won't work anymore.

Just trying to let other folks know what works and what doesn't. I
think you knew this already, just that you didn't touch all the bases
when you mentioned it originally.

class Test
{
private boolean set;
private int data;

void methodA()
{
data = 5;
synchronized( this ) {
set = true;
}
}

void methodB()
{
synchronized( this ) {
if (set)
{
System.out.print(data); // may print "0"
}
}
}
}
From: markspace on
Peter Duniho wrote:
> markspace wrote:
>> Joshua Cranmer wrote:
>>
>>> Immutability is the easiest way to guarantee safe publication: final
>>> fields guarantee that they can be used by any thread safely.
>>
>> Note quite. Final fields by themselves don't guarantee immutability
>> or safe publication. An immutable object must:
>>
>> * All fields are final.
>> * It is properly constructed. (No "this escapes." That bit is
>> important.)
>> * Its state cannot be modified after construction.
>>
>> Those three things must hold for an object to be treated as immutable
>> by the JVM. Mess up any one, and poof, no more immutability.
>
> I would be interested in seeing an example where your third point is
> violated but the first point is not.


class Mutable {

private final List<String> names;

public Mutable() {
names = new List<String>();
names.add("Joe");
names.add("Jim");
}

public List<String> getNames() {
return names;
}
}

This class would be immutable except for the fact that the private final
variable "names" escapes and could be modified. If the getNames()
method is removed, then the class is immutable and can be published even
in a data race.

And I know what's coming next, but it really is immutable if the getter
is removed. The JLS was designed to work this way because String has to
be immutable, and String initializes itself the same way, so the JLS was
constructed to make this kind of initialization safe and immutable.

From: Lew on
Peter Duniho wrote:
>> class Test
>> {
>> private volatile boolean set = false;
>>
>> In the above example, if methodA() is executed on one thread, and then
>> methodB() is executed in a different thread, methodB() is guaranteed
>> to see the new value for "data".

markspace wrote:
> Yes, for volatile, this works. Volatile fields have special semantics
> which prevent re-ordering reads and writes by the compiler, so this is
> guaranteed to work.
>
> I said it didn't work for all of the cases Joshua listed, particularily
> the synchronized one. Replace the volatile with a synchronized block and
> it won't work anymore.

It will so, as long as they synchronize on the same lock or monitor.

> Just trying to let other folks know what works and what doesn't. I think
> you knew this already, just that you didn't touch all the bases when you
> mentioned it originally.
>
> class Test
> {
> private boolean set;
> private int data;
>
> void methodA()
> {
> data = 5;
> synchronized( this ) {
> set = true;
> }
> }
>
> void methodB()
> {
> synchronized( this ) {
> if (set)
> {
> System.out.print(data); // may print "0"
> }
> }
> }
> }

You're mistaken. The assignment in 'methodA()' of 'data' /happens-before/ the
write to 'set', which latter is synchronized. The read in 'methodB()' of
'data' /happens-after/ the read of 'set', which latter is synchronized on the
same monitor. Therefore the read is guaranteed to see the write.

--
Lew