Concurrent Serialization Pattern

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

Concurrent Serialization Pattern

Kevin Condon
The Concurrency Puzzle thread inspired some questions in my mind about
serializing and deserializing objects to safely handle concurrency
issues.  I'd like to hear what patterns others are following, as well
as correcting any incorrect notions I may have developed.  Here's my
simple (and naive) example class, which I'd guess isn't far from a lot
of existing production code:

public class Serial implements Serializable {
  private static final long serialVersionUID = 2006091400L;
  private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  private int x;

  public int getX() {
    lock.readLock().lock();
    try {
      return x;
    } finally {
      lock.readLock().unlock();
    }
  }

  public void setX(int x) {
    lock.writeLock().lock();
    try {
      // imagine some long running op here ...
      this.x = x;
    } finally {
      lock.writeLock().unlock();
    }
  }
}

While this example handles ordinary run-time concurrency fine, it does
not guarantee that the most current value of x would be serialized.
So I'd need to add:

  private void writeObject(ObjectOutputStream out) throws IOException {
    lock.readLock().lock();
    try {
      out.defaultWriteObject();
    } finally {
      lock.readLock().unlock();
    }
  }

Easy enough.  But upon deserialization, the deserialized value of x
would not be guaranteed to be visible in threads other than the thread
invoking in.readObject().  This is because there is no happens-before
relationship between the deserializing thread and concurrent threads
invoking getX() without using lock.  To fix this, I'll need to add a
readObject() method that uses lock.  That will require additional
supporting changes, too:

  private transient int x;  // emit using custom serialization
  ...

  private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    lock.readLock().lock();
    try {
      out.writeInt(x);
    } finally {
      lock.readLock().unlock();
    }
  }

  private void readObject(ObjectInputStream in)
      throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    lock.writeLock().lock();
    try {
      x = in.readInt();
    } finally {
      lock.writeLock().unlock();
    }
  }

And viola!  There's the pattern.  I'd summarize the principles this way:

1. Make locks immutable fields, using the final modifier and rely on
the default serialization handling to create the necessary
happens-before relationships for lock field value visibility.  (See
JLS 17.5.3.)

2. Use a custom serialization form (see Effective Java, Item 55) and
make sure to create happens-before relationships on all
serialized/deserialized fields by using the same locking required for
ordinary run-time access.

Note that the concurrent lock could be replaces with a class Mutex
implements Serializable {} to use simple Object monitor locking
instead.

One question does loom in my mind here:  Is it safe to serialize and
deserialize a ReentrantReadWriteLock object?  My tests with
serializing one while the lock was held didn't create any blocking
upon deserialization, but I'd like to know if there's any potential
issue with this.  I have the same question with using Object monitor
locking.

Any thoughts on this pattern?  Is anyone using something different or
better?  Does anyone else feel a little queezy about code they've
written prior to thinking through this? ;)

Regards,
Kevin Condon
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Thomas Hawtin
Kevin Condon wrote:

>
> And viola!  There's the pattern.  I'd summarize the principles this way:
>
> 1. Make locks immutable fields, using the final modifier and rely on
> the default serialization handling to create the necessary
> happens-before relationships for lock field value visibility.  (See
> JLS 17.5.3.)
>
> 2. Use a custom serialization form (see Effective Java, Item 55) and
> make sure to create happens-before relationships on all
> serialized/deserialized fields by using the same locking required for
> ordinary run-time access.

In 1.6, java.util.Random (resetSeed method) uses sun.misc.Unsafe to poke
it's final, (effectively) transient seed field. Not something I would
generally recommend.

If you don't have to worry about a serialisable base class, then it
might be simpler just to introduce a non-serialisable, package private
base class.

abstract class SerialBase { // Must not implement Serializable
     final ReadWriteLock lock = new ReentrantReadWriteLock();

     SerialBase() {
     }
}

public class Serial extends SerialBase implements Serializable {
     private static final long serialVersionUID = 2006091401L;

     private int x;

     private void writeObject(
         ObjectOutputStream out
     ) throws IOException {
         lock.readLock().lock();
         try {
             out.defaultWriteObject();
         } finally {
             lock.readLock().unlock();
         }
     }
     private void readObject(
         ObjectInputStream in
     ) throws IOException, ClassNotFoundException {
         lock.writeLock().lock();
         try {
             in.defaultReadObject();
         } finally {
             lock.writeLock().unlock();
         }
     }
}

Tom Hawtin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
Hi Tom,

On 9/14/06, Thomas Hawtin <[hidden email]> wrote:
> In 1.6, java.util.Random (resetSeed method) uses sun.misc.Unsafe to poke
> it's final, (effectively) transient seed field. Not something I would
> generally recommend.

Agreed.  I'd much rather rely on the default serialization "magic" to
ensure that the final fields never publish anything other than the
deserialized field values.  JLS certainly discourages non-wizards from
exercising such magic.  :)

> If you don't have to worry about a serialisable base class, then it
> might be simpler just to introduce a non-serialisable, package private
> base class.

Ahh, that would be a clever way to avoid implementing the custom
serialization.  I've actually made it a habit to make serializable
state fields transient and doing the custom serialize/deserialize code
(based on the recommendations in Efffective Java), but I can see why
someone might not think the extra work required was worthwhile in
every case.

Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
Sorry, I just realized that there's a simpler solution.  Really just
need to make lock a transient final.  Then it doesn't have to be
serialized and you're not forced to use custom serialization if you
don't want to.  I didn't realize that the final sematics had
precedence over the transient qualifier, so I incorrectly thought that
lock would have the default null value after deserialization.

public class Serial implements Serializable {
 private static final long serialVersionUID = 2006091400L;
 private transient final ReentrantReadWriteLock lock =
    new ReentrantReadWriteLock();
 private int x;

 public int getX() {
   lock.readLock().lock();
   try {
     return x;
   } finally {
     lock.readLock().unlock();
   }
 }

 public void setX(int x) {
   lock.writeLock().lock();
   try {
     // imagine some long running op here ...
     this.x = x;
   } finally {
     lock.writeLock().unlock();
   }
 }

 private void writeObject(ObjectOutputStream out) throws IOException {
   lock.readLock().lock();
   try {
     out.defaultWriteObject();
   } finally {
     lock.readLock().unlock();
   }
 }

 private void readObject(ObjectInputStream in)
     throws IOException, ClassNotFoundException {
   lock.writeLock().lock();
   try {
     in.defaultReadObject();
   } finally {
     lock.writeLock().unlock();
   }
 }
}

Are there any problems with this simplified pattern?  Especially, is
there any risk of readObject() seeing the default null value for lock
and getting an NPE?  (I don't think there is, but that's at the heart
the whole concurrency puzzle thread.)  Are there other alternatives?

Regards,
Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Bob Lee-8
On 9/14/06, Kevin Condon <[hidden email]> wrote:
> Really just
> need to make lock a transient final.  Then it doesn't have to be
> serialized and you're not forced to use custom serialization if you
> don't want to.  I didn't realize that the final sematics had
> precedence over the transient qualifier, so I incorrectly thought that
> lock would have the default null value after deserialization.

Holy cow, I always incorrectly thought that, too! Off to clean up some code...

Bob
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

David Holmes-3
In reply to this post by Kevin Condon
Kevin,

> I didn't realize that the final sematics had
> precedence over the transient qualifier, so I incorrectly thought that
> lock would have the default null value after deserialization.

According to my reading of the serialization spec it *should* have a null
value. transient fields don't get serialized, unless you custom serialize
them. So a transient final field should have its default initialized value
when readObject is invoked.

Something seems amiss here.

David Holmes

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Bob Lee-8
It only works for constants. :(

On 9/14/06, David Holmes <[hidden email]> wrote:

> Kevin,
>
> > I didn't realize that the final sematics had
> > precedence over the transient qualifier, so I incorrectly thought that
> > lock would have the default null value after deserialization.
>
> According to my reading of the serialization spec it *should* have a null
> value. transient fields don't get serialized, unless you custom serialize
> them. So a transient final field should have its default initialized value
> when readObject is invoked.
>
> Something seems amiss here.
>
> David Holmes
>
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
>
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

David Holmes-3
Well not quite. If the final field is a compile-time constant then the
bytecode doesn't contain any field access expressions it simply uses the
compile-time constant value directly. The actual
serialization/deserialization is unaffected. As a test, serialize an
instance when the constant has value A, recompile the class with the
constant set to B, read the old serialized instance back in and "access" the
final field - you will see B.

David Holmes

> -----Original Message-----
> From: [hidden email] [mailto:[hidden email]]On Behalf Of
> Bob Lee
> Sent: Friday, 15 September 2006 4:17 PM
> To: [hidden email]
> Cc: Kevin Condon; [hidden email]
> Subject: Re: [concurrency-interest] Concurrent Serialization Pattern
>
>
> It only works for constants. :(
>
> On 9/14/06, David Holmes <[hidden email]> wrote:
> > Kevin,
> >
> > > I didn't realize that the final sematics had
> > > precedence over the transient qualifier, so I incorrectly thought that
> > > lock would have the default null value after deserialization.
> >
> > According to my reading of the serialization spec it *should*
> have a null
> > value. transient fields don't get serialized, unless you custom
> serialize
> > them. So a transient final field should have its default
> initialized value
> > when readObject is invoked.
> >
> > Something seems amiss here.
> >
> > David Holmes
> >
> > _______________________________________________
> > Concurrency-interest mailing list
> > [hidden email]
> > http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
> >

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
David,

On 9/15/06, David Holmes <[hidden email]> wrote:
> As a test, serialize an
> instance when the constant has value A, recompile the class with the
> constant set to B, read the old serialized instance back in and "access" the
> final field - you will see B.

That was exactly the test I used when I empirically discovered the
unexpected behavior.

> > > According to my reading of the serialization spec it *should*
> > have a null
> > > value. transient fields don't get serialized, unless you custom
> > serialize
> > > them. So a transient final field should have its default
> > initialized value
> > > when readObject is invoked.

I'm not entirely content with empirical discovery for understanding
JMM issues, and the serialization spec *does* seem to give an
expectation of seeing the default value for the transient final field:
 "The values of every field of the object whether transient or not,
static or not are set to the default value for the fields type"  (sec
3.4).  Though it doesn't explicitly mention "final" fields.  Since
"Fields declared as transient or static are ignored by the
deserialization process" according to the ObjectInputStream javadoc, I
guess it makes sense for "ignored" to mean that transient fields are
left in the "completely initialized" state that Class.newInstance()
leaves them in (JLS 17.5).  So the guarantee for never seeing the
transient final field's default value is provided by JLS 16 and 17.5.
Maybe an explicit statement about transient final fields would help in
the serialization spec and in the ObjectInputStream API javadoc?

I wonder why there's such an expectation that transient final fields
would have their default value after deserializing.  Do older
compilers or JVMs behave differently?  Or is it just the clarity of
the docs, esp. the serialization spec sec 3.4?

Regards,
Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
On 9/15/06, Kevin Condon <[hidden email]> wrote:
> I wonder why there's such an expectation that transient final fields
> would have their default value after deserializing.  Do older
> compilers or JVMs behave differently?  Or is it just the clarity of
> the docs, esp. the serialization spec sec 3.4?

I just tried compiling and running my tests with Sun's 1.1.8, 1.3.1,
and 1.4.1 JVMs, and got the same results as with 1.5.  Guess it's a
matter of interpreting the docs.

Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Peter Jones-9
In reply to this post by Kevin Condon
>>>> According to my reading of the serialization spec it *should*
>>>> have a null value. transient fields don't get serialized, unless
>>>> you custom serialize them. So a transient final field should have
>>>> its default initialized value when readObject is invoked.
>
> I'm not entirely content with empirical discovery for understanding
> JMM issues, and the serialization spec *does* seem to give an
> expectation of seeing the default value for the transient final
> field: "The. values of every field of the object whether transient
> or not, static or not are set to the default value for the fields
> type" (sec 3.4). Though it doesn't explicitly mention "final"
> fields.

That assertion is accurate even for final fields.  If you reflect upon
a transient final instance field of a deserialized object-- and it
hasn't been set through privileged magic-- you will see the default
value (null, 0, or false).

The case David cited is that if a final field's initializer is a
compile-time constant expression (JLS3 15.28), so the field is a
constant variable (JLS3 4.12.4), a field access expression for it will
be compiled to the compile-time constant value (i.e. inlined) rather
than to a bytecode to access the field at runtime (JLS3 13.4.9)-- so a
non-reflective field access won't "see" the default value that it has.

> Since "Fields declared as transient or static are ignored by the
> deserialization process" according to the ObjectInputStream javadoc,
> I guess it makes sense for "ignored" to mean that transient fields
> are left in the "completely initialized" state that
> Class.newInstance() leaves them in (JLS 17.5).

No, Class.newInstance() invokes the default constructor and thus
executes instance field initializers, unlike deserialization.

> So the guarantee for never seeing the transient final field's
> default value is provided by JLS 16 and 17.5.  Maybe an explicit
> statement about transient final fields would help in the
> serialization spec and in the ObjectInputStream API javadoc?
>
> I wonder why there's such an expectation that transient final fields
> would have their default value after deserializing.  Do older
> compilers or JVMs behave differently?  Or is it just the clarity of
> the docs, esp. the serialization spec sec 3.4?

There is no such guarantee-- it just can seem that way with
constant-initialized final fields.  Documentation could certainly be
improved to reduce confusion in this area; there is an open RFE about
related points:

        http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6252102

-- Peter
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Peter Jones-9
In reply to this post by Kevin Condon
>   private transient int x;  // emit using custom serialization
>   ...
>
>   private void writeObject(ObjectOutputStream out) throws IOException {
>     out.defaultWriteObject();
>     lock.readLock().lock();
>     try {
>       out.writeInt(x);
>     } finally {
>       lock.readLock().unlock();
>     }
>   }
>
>   private void readObject(ObjectInputStream in)
>       throws IOException, ClassNotFoundException {
>     in.defaultReadObject();
>     lock.writeLock().lock();
>     try {
>       x = in.readInt();
>     } finally {
>       lock.writeLock().unlock();
>     }
>   }

[snip]

> Any thoughts on this pattern?

I would probably move the invocations on the streams "out"/"in"
outside the lock/unlock, so that you're not holding the lock during a
potentially blocking I/O operation on an arbitrary stream (at least
for the writing case, when some other thread might want the lock).
(And then I think you would have blocks of code similar to what's in
getX/setX, which you might want to share in private methods.)

If you would rather not write "x" as custom serialized form data, you
could use the ObjectOutputStream.putFields/PutField/writeFields and
ObjectInputStream.readFields/GetField APIs to still write it as the
serializable field named "x"-- for what it's worth.

-- Peter
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
In reply to this post by Peter Jones-9
Oops, sorry -- my tests were with a primitive (transient final int),
but a transient final *object* is left at the default null value after
defaultReadObject() returns.  (Note to self:  Test *exactly* the case
you're trying to prove from now on.)  So none of my previous examples
actually work, since they all rely on the transient final
ReentrantReadWriteLock = ..., which is going to be the default null.
:(

Thomas Hawtin's earlier suggestion of extending a non-serializable
base class that declares the lock as a final variable is the only
working solution thus far, and at times isn't even possible due to
single-inheritance limitations.

Here's a solution using readResolve() that I tested successfully.  It
uses the correct synchronization, and provides a lot of flexibility.
(Credit to coworker Michael Smith who mentioned off-the-cuff, "maybe
you have to use readResolve".)

public class Serial2 implements Serializable {
  private static final long serialVersionUID = 2006091500L;
  private transient final ReentrantReadWriteLock lock =
    new ReentrantReadWriteLock();
  private transient int x;

  public int getX() {
    lock.readLock().lock();
    try {
      return x;
    } finally {
      lock.readLock().unlock();
    }
  }

  public void setX(int x) {
    lock.writeLock().lock();
    try {
      this.x = x;
    } finally {
      lock.writeLock().unlock();
    }
  }

  private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    out.writeInt(getX());
  }

  private void readObject(ObjectInputStream in)
      throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    // lock is null, so don't invoke this.setX();
    // program order will ensure visibility
    x = in.readInt();
  }

  private Object readResolve() throws ObjectStreamException {
    Serial2 s = new Serial2();
    // lock is null, so don't invoke this.getX();
    // program order will ensure visibility
    s.setX(x);
    return s;
  }
}

Seems a bit complex and nuanced for serializing such a simple class.

Regards,
Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

David Holmes-3
Kevin,

> Here's a solution using readResolve() that I tested successfully.

I've somewhat lost the original problem that was being solved :) The issue
with final fields and serialization is that if you need to perform custom
deserialization of the final field then you can't do it directly. You then
have two alternatives that I am aware of:

1. You use custom deserialization, reflection and setAccessible(true) to set
the final field to the desired value; or
2. You use readresolve to replace the deserialized object with an equivalent
one that you can construct with all the right final field values

The issue I believe you were trying to address in your serialization code
was the possibility of unsafe-publication of the deserialized object. As you
indicated originally if the deserialization process uses the same lock that
is used to access the object's state then there can be no races. Hence
readObject deserializes while holding the writeLock. writeObject also uses
the readLock to ensure a consistent snapshot of the object's state (though
the idea that you would serialize an object while it is being actively
mutated is a little worrying :) ). So your pattern made the object immune to
unsafe-publication - though as we have discussed in the context of
constructors, generally it is not worth the effort to achieve this level of
thread safety - just ensure you do publish safely.

Now somewhere a long the way you decided that your final Lock field needed
to be transient. That is the part I don't understand as
ReentrantReadWriteLock is Serializable.

Cheers,
David Holmes

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
Hi David,

On 9/16/06, David Holmes <[hidden email]> wrote:
[snip]
> So your pattern made the object immune to
> unsafe-publication - though as we have discussed in the context of
> constructors, generally it is not worth the effort to achieve this level of
> thread safety - just ensure you do publish safely.

Maybe I've misunderstood the JMM.  I was under the impression that
non-final field values set during deserialization and construction
would be in a data race unless a happens-before trigger is introduced.
 This would be true even if the object were "safely published", which
I take to mean that no reference to the object is made accessible to
other threads until construction/deserialization is complete.  Wasn't
that the gist of the concurrency puzzle thread and the possibility of
seeing the default value w/o a hb?

> Now somewhere a long the way you decided that your final Lock field needed
> to be transient. That is the part I don't understand as
> ReentrantReadWriteLock is Serializable.

If I've correctly understood the JMM on this (and I guess the verdict
is still out on that), then the same locking for "normal" access must
be used during construction, serialization, and deserialization.  Thus
my quandry, since I can't use a serialized lock object until after
it's been deserialized.  But then it's too late to use it as the hb
trigger for the other values already deserialized with it.  That's the
problem I'm trying to solve.

I've thought of 2 possibly better ways to address this than the
readResolve approach, but I'll hold off until it's confirmed that the
problem I'm solving is even real.

Regards,
Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

David Holmes-3
Hi Kevin,

> Maybe I've misunderstood the JMM.  I was under the impression that
> non-final field values set during deserialization and construction
> would be in a data race unless a happens-before trigger is introduced.
>  This would be true even if the object were "safely published", which
> I take to mean that no reference to the object is made accessible to
> other threads until construction/deserialization is complete.

Safe publication means more than construction/deserialization being
"complete" it means that you establish a happens-before relationship between
the construction/deserialization and any subsequent use.

> If I've correctly understood the JMM on this (and I guess the verdict
> is still out on that), then the same locking for "normal" access must
> be used during construction, serialization, and deserialization.  Thus
> my quandry, since I can't use a serialized lock object until after
> it's been deserialized.  But then it's too late to use it as the hb
> trigger for the other values already deserialized with it.  That's the
> problem I'm trying to solve.

You must set up the following:

construction/deserialization happens-before publication happens-before
subsequent-use

If the "publication" phase involves no synchronization (ie is unsafe
publication) then it is as you have stated - the same synchronization
mechanism needs to be used in deserialization and subsequent use so that the
publisher and user have "synchronized" appropriately. So yes, even if the
lock is serialized you would still need to use custom deserialization to
actually use the lock. So your readObject could be:

    defaultReadObject();
    lock.writeLock().acquire();
    lock.writeLock().release();

However if you use safe-publication then the custom deserialization is not
needed.

Does the above clarify things?

Cheers,
David Holmes

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Concurrent Serialization Pattern

Kevin Condon
Hi David,

I don't know how I missed that.  I was so busy thinking about the
internals of the class that I failed to notice that synchronizing
references to the object would create the happens-before for values
set during construction/deserialization.  Now I understand why you
said a couple of posts ago, "generally it is not worth the effort to
achieve this level of thread safety - just ensure you do publish
safely."

Clarification complete.  :)

Thanks,
Kevin
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest