[concurrency-interest] question the about the JMM

Brian Goetz brian at briangoetz.com
Wed Dec 5 16:56:55 EST 2007


> I'm having a discussion with a a very smart colleague about some
> example (from iBatis) that in my opinion has a visibility problem and
> in his opinion doesn't.

I reviewed your colleague's e-mail again.  I had mistakenly switched the 
positions: I thought _he_ was saying "there is a visibility problem" and 
his colleague (you, apparently) that there was not.  But my intention 
was to say "this code is broken", and the comments in the message should 
make that clear.

Unless something() is called from the same thread as setFlag(), it is 
not guaranteed to _ever_ see flag be true unless it acquires the lock 
acquired by the writing thread.

Oh, and by the way, in:

 >    synchronized (new Object()) {
 >      // unimportant
 >    }

the synchronization has no effect in any case.  The compiler can prove 
that no other thread could synchronize on the same object, and is thus 
permitted to eliminate the sync entirely.

On a tangentially related note, it is in very poor taste to forward a 
private message to a public list without the author's permission; doubly 
so because it was sent to someone else, not you.


> 
> This is the example:
> 
>  private boolean flag = true;
> 
>  public void something() {
>    if (flag) {
>      synchronized (this) { // synchronized block
>        // do something
>      }
>    } else {
>      // do something else with very little impact, NOT inside a
>  synchronized block
>    }
>  }
> 
> public void setFlag(boolean newFlag) {
>    this.flag = newFlag;
>    synchronized (new Object()) { // ANOTHER MONITOR THAN IN METHOD something
>      // unimportant
>    }
>  }
> 
> With my current understanding of the JMM I would expect that the flag
> variable has a visibility problem. Because there is no happens before
> relation between the write in the setFlag method, and the read in the
> something method, a thread that runs the something method doesn't need
> to see the written value by a different thread ever.
> 
> The problem could be solved by using the the monitor lock rule (using
> the lock of the object itself) or the volatile read/write rule. So the
> simplest thing to remove the visibility problem is to make the flag
> volatile.
> 
> The question is, am I correct? Does this example contain a visibility problem?
> 
> To make things more complicated, we have asked Brian Goetz and
> according to him my colleague is right.
> 
> this is the mail conversion between them:
> 
> rom: Brian Goetz [mailto:brian at briangoetz.com]
> Sent: Wed 12/5/2007 16:41
> To: Erwin Bolwidt
> Subject: Re: JMM question (wrt to a statement you made at JavaPolis)
> 
> Your colleague is wrong.
> 
> Both of the statements you refer to are correct.  The key is that only
> synchronization actions (reads and writes of volatiles, and acquire and
> release of locks) need to be totally ordered.  So if thread A release a
> lock "before" thread B acquires one, that doesn't mean anything, because
> the threads don't share your concept of "before".
> 
> The primary mistake people make in approaching concurrency is to falsely
> assume "sequential consistency", meaning that "everything happens in a
> fixed, global order".  In reality, it is more like the thought
> experiments you did in special relativity, where ordering is relative to
> the observer.  This happens in modern CPUs too, in the absence of
> adequate synchronization.  The only total ordering you can rely on is
> synchronization actions.  This is why if we synchronize on a common
> lock, things work properly -- because it is sensible to speak of "when
> you release the lock and I subsequently acquire it."  When we acquire
> different locks, there is no way to guarantee "subsequent".  (And when
> we don't acquire locks at all, it isn't even meaningful to talk about
> "subsequent.")
> 
> Any logic that relies on unsynchronized reads of shared references to
> mutable objects is committing the same error as double-checked locking,
> with the same possible consequences.
> 
> The rules for the new memory model (happens-before) are actually fairly
> simple to reason through.  Its the "I (think I) know what's going on in
> the machine" part that leads people into trouble.  Such reasoning
> invariably amounts to "I don't believe these are really the rules."
> 
> Erwin Bolwidt wrote:
>> Hi Brian,
>>
>> A collegue and are having a discussion about one aspect of the new Java
>> Memory Model.
>> After one of your presentations on the JMM I asked you about how
>> specific volatile or synchronized is (at JavaPolis I believe but it
>> could have been JavaOne)
>>
>> Specifically, I wondered if synchronized on different monitors or access
>> to different volatile variables were really independent: if these would
>> only cause related data to be synchronized (written out to/invalidated
>> L1/2 caches) with "global" memory or if they would effectively
>> synchronize the whole thread state (caches) with "global" memory.
>> You response as I remember it was that the latter was the case: the
>> whole thread state (caches) would be synchronized. The former would be
>> too complicated to implement.
>>
>> The JSR 133 FAQ (co-authored by you) at
>> http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html states:
>>
>>         *Important Note:* Note that it is important for both threads to
>>         synchronize on the same monitor in order to set up the
>>         happens-before relationship properly. It is not the case that
>>         everything visible to thread A when it synchronizes on object X
>>         becomes visible to thread B after it synchronizes on object Y.
>>         The release and acquire have to "match" (i.e., be performed on
>>         the same monitor) to have the right semantics. Otherwise, the
>>         code has a data race.
>>
>> Now I realize that possibly both statements are true, and that this
>> boils down to a misunderstanding of terms like "happens-before
>> relationship".
>>
>> I have an example and I'm not sure what would happen here. In the real
>> case, "flag" was called "cacheEnabled" and the question was whether the
>> cache could be turned off in a way that would be guaranteed to work.
>> This looks a little like the double-check locking issue. My statement
>> was that if "setFlag(false)" was called on one thread, the something()
>> method on another thread would be guaranteed to see this
>> _the_second_time_ the method was entered, because the synchronization
>> would invalidate the CPU caches on the thread that runs something(), and
>> likewise the synchronized on a different monitor in setFlag would write
>> out the change to the field "flag" to global memory.
>>
>> My collegue disagrees. Can you tell who is right?
>>
>> Thanks a lot if you can spend some time on this.
>>
>> Regards,
>>   Erwin Bolwidt
>>
>> -------------- Example code --------------
>>
>> private boolean flag = true;
>>
>> public void something() {
>>   if (flag) {
>>     synchronized (this) { // synchronized block
>>       // do something
>>     }
>>   } else {
>>     // do something else with very little impact, NOT inside a
>> synchronized block
>>   }
>> }
>>
>> public void setFlag(boolean newFlag) {
>>   this.flag = newFlag;
>>   synchronized (new Object()) { // ANOTHER MONITOR THAN IN METHOD something
>>     // unimportant
>>   }
>> }
>>
>>
>> --
>> Erwin Bolwidt
>> business consultant
>>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at altair.cs.oswego.edu
> http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest


More information about the Concurrency-interest mailing list