[concurrency-interest] Concurrent Serialization Pattern

Kevin Condon conivek at gmail.com
Thu Sep 14 11:32:42 EDT 2006


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


More information about the Concurrency-interest mailing list