On Tue, 7 Aug 2001, Michael Mok wrote:

> Hi all
> 
> This may be an off topic discussion. Craig mentioned about the need to make
> session attributes thread safe.
> 

Thought that might catch someone's attention ... :-)

> What are the factors to look out for in order to make session attributes
> thread safe?
> 

It's worthwhile to first review a couple of reasons why you should care
about this.  Consider the following scenarios:

* Your application uses frames (when you refresh the entire page,
  the browser will often issue multiple simultaneous requests for
  the different frames).

* Your user opens multiple windows to the same application (which will
  be part of the same session in most cases), and is performing requests
  on more than one window at the same time.

* Your user starts a long-running request, presses stop, and then starts
  a different request before the first one has finished.

In each case, you will have multiple requests accessing the same session
(and therefore the same session attributes.  So, how worried should you
be?

The general principle I use is "enlightened paranoia" -- will your
application give back to the correct results, even if the worst possible
combination of interactions from the multiple requests happpens?

Most of the time, you are concerned about requests that *modify* a session
attribute causing problems for some other request that is either modifying
the same attribute at the same time, or is trying to read stuff from that
attribute while some other request is writing to it.   Let's take the
"multiple writes" problem first.

If your property is a simple type (a primitive type or a String), you
generally don't need to worry too much.  Whichever assignment happens last
wins.  But there won't be the case that a pointer to the underlying value
is "halfway" updated so that a simultaneous reader gets a totally bogus
value.  In technical terms, assignments of Strings and primitives can be
considered "atomic" in Java.  On the other hand, if the underlying
property is complex (such as a linked list), it is clearly possible for
simultaneous writes (or a write that is simultaneous with one or more
reads) to interfere with each other.

Multiple simultaneous "read" operations are generally not a problem,
because they usually don't cause any change to the internal structure of
the objects being read.

The situation gets more interesting when there are one or more read
operations that are going on simultaneous with a write operation.  Again,
primitives and Strings are not the problem -- but complex data types
are.  Just as an example, consider a property represented as a linked list
that is being modified by one request while being read by another.  It is
very easy to conceive of how the pointers to the "next" and/or
"previous" elements could be modified at an inopportune time while the
reading request is trying to process the list.

The general strategy when you have the potential for thread safety
problems is to use the "synchronized" modifier in your Java
classes.  You don't want to do this universally, because there is a
performance cost -- so you want to be diligent about understanding where
it is needed.  Synchronization can be done at two levels:

* You can add "synchronized" to the getter and setter methods themselves.
  In effect, you are allowing only a single getter or setter operation
  on the entire object containing these methods.  It works, but sometimes
  it is overkill.  (In technical terms, synchronizing a method is the same
  as creating a block like this:

    synchronized (this) {
      ... contents of this method ...
    }

  because it locks access to this entire object instance.)

* You can create a "synchronized" block around access to a particular
  instance variable.  In this sceanrio you will be serializing access to
  a particular instance variable, without impeding access to other
  variables in the same session attribute.  This can improve performance
  over the case where you synchronize the methods themselves, but this
  is only noticeable if multiple simultaneous access really does happen.

Note that, in some cases, the underlying classes you are using take care
of synchronization for you.  For example, java.util.Hashtable guarantees
that any "get()" or "put()" operation you execute cannot corrupt other
"get" or "put" operations that are happening simultaneously.  On the other
hand, the "java.util.HashMap" class (with very similar
functionality) makes no such guarantees -- synchronization is up to the
caller.  This can really help performance in single-threaded applications
that (by definition) cannot have multiple threads accessing the same
object at the same time, but it becomes your responsibility to worry about
it.

    NOTE:  The servlet API requires that a container guarantee
    the thread-safety of calls like session.setAttribute().  Your
    application does *not* have to synchronize those calls - it
    does need to worry about what happens when two requests call
    session.getAttribute() and then calling methods on the same
    attribute at the same time.

So, we can summarize "safe threading practices" like this:

* If you are dealing with property values of native types (or Strings),
  you generally do not need to worry about thread safety issues (unless
  your application requires that changes to multiple properties be
  visible "all at the same time" or "not at all").

* If you are dealing with properties that have a complex internal
  structure, you will generally have to synchronize all accesses to
  those properties (unless the underlying property class takes care
  of these issues for you).

* Always assume that your session attributes will be accessed by multiple
  requests at the same time, and that those accesses will happen at the
  worst possible times.  Use synchronization as needed to make sure that
  your application functions correctly in those scenarios.

* Instance variables in a servlet (or an Action, for a Struts-based
  application) have exactly the same multiple thread concerns as
  session attributes.

* On the other hand, local variables (inside a method), and request
  attributes, are guaranteed to be accessed by only one thread at a
  time *unless* you initialize them to point at some instance (i.e.
  instance variable or session attribute) that is itself subject to
  multiple thread access).

The entire field of multithread programming is substantially more complex
than this brief overview, but it should give you a good starting point for
understanding what you need to worry about, and what you don't.  You'll
know you've arrived as a "multi-thread-aware" developer when you
understand what statements like "double checked locking doesn't
work" mean.  :-)

> Thanks in advance
> 
> Regards
> 
> Michael Mok
> 

Craig

Reply via email to