On Fri, 15 Oct 1999, Peter Pilgrim wrote:

> In fact code is wrong, at least in the conditionals statements.
> Sorry about that
> 
>       public static NetworkPrinter getInstance()
>        {
>               // Point *A*
>                if ( thePrinter == null ) {
>                   // Thread Safety - Double Guard technique
>                 // Point *B*
>                   synchronized( locker ) {
>                       // Point *C*
>                       // SHOULD BE A CRITICAL SECTION                 
>                        if ( thePrinter == null ) {
>                             thePrinter = new NetworkPrinter();
>                   }
>                }
>             return (thePrinter);
>       }
> 
> I can't understand what is the problem with the double guard technique.
> You synchronize threads on the `locker' object and that should be 
> enough. In fact it should act like a `Mutex' (mutual exclusion).

You are not synchronizing threads on the locker object.  If you
were doing that, you would have synchronization on the locker object
for each thread that tried to use thePrinter.  You don't.  Nothing
stops one thread from still being in the synchronized section while
another thread uses a partially stored result.

> If you have two threads t1 and t2 racing to create the singleton
> which is not yet inited yet at Point A, both will get to Point B.
> However only one of the two threads, assume by random choice t2, 
> will get to Point C, because of the use of the synchronized object 
> `locker'. Thread t2 will be allowed access to the critical section,
> and t1 will be blocked until t2 leaves the critical section.
> t2 will create the singleton and return it to its calling 
> thread. As it leaves the monitor `locker' it will unblock t1
> from waiting, t1 will enter the critical section, and 
> discovers that the singleton `thePrinter' already exists.
> It therefore returns the same singleton that t2 created.
> 
> So converning invisiblity and visibility what's the problem?

Your mistake is in assuming that just because the thePrinter reference is
visible, everything that it references is visible, ie. the complete object
is visible.  That is _not_ necessarily the case because Java does _not_
guarantee that all changes will be visible just because one is and because
you have no synchronization on the thread that sees the thePrinter
reference before entering the synchronized section.

The thePrinter reference is not everything you need to be able to see, you
have to be able to see the object itself as well.

You need to read chapter 17 of the JLS very carefully:

http://java.sun.com/docs/books/jls/html/17.doc.html#30206

It summarizes things as:

    - Proper use of synchronization constructs will allow reliable
      transmission of values or sets of values from one thread to
      another through shared variables.

    - When a thread uses the value of a variable, the value it
      obtains is in fact a value stored into the variable by that
      thread or by some other thread. This is true even if the program
      does not contain code for proper synchronization. For example,
      if two threads store references to different objects into the
      same reference value, the variable will subsequently contain
      a reference to one object or the other, not a reference to some
      other object or a corrupted reference value. (There is a special
      exception for long and double values; see 17.4.)

    - In the absence of explicit synchronization, a Java implementation
      is free to update the main memory in an order that may be
      surprising. Therefore the programmer who prefers to avoid
      surprises should use explicit synchronization.

"a Java implementation is free to update the main memory in an order that
may be surprising."  

In another section where it describes the constraints for actions on 
a variable by a thread, it states:

        Provided that all the constraints above and below are obeyed, a
        load or store action may be issued at any time by any thread on
        any variable, at the whim of the implementation.

Section 17.8 goes on:

        If a variable is not declared volatile, then the rules in the
        previous sections are relaxed slightly to allow store actions to
        occur earlier than would otherwise be permitted. The purpose of
        this relaxation is to allow optimizing Java compilers to perform
        certain kinds of code rearrangement that preserve the semantics of
        properly synchronized programs but might be caught in the act of
        performing memory actions out of order by programs that are not
        properly synchronized.

See also section 17.11 for a sample program demonstrating the effects 
of out of order writes.

(I'll be quiet now and won't be offtopic any more...)


----------------------------------------------------------------------
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to