[concurrency-interest] Soliciting input about removeAllThreadLocals

Luke Blanshard blanshlu@netscape.net
Sun, 08 Dec 2002 11:54:47 -0600


  Many years ago, when I was a young programmer, an older and wiser man 
explained one of the most important precepts of good programming to me 
thus:  Global Variables Are Bad.

  Like all such rules, this one has exceptions.  But with this precept 
in my mind, I generally ask myself some tough questions when tempted to 
create a static variable.  In particular: "What is it about this 
variable that is aligned with every process that might run this code? 
 Are you absolutely sure you will never want two different values for 
this variable within the same process?"

  The usual reason for creating a global variable is laziness.  The 
second most usual reason is unjustified optimization.

  All of this is by way of explaining why I avoid ThreadLocal like the 
plague.  Thread-local variables are just global variables in disguise. 
 The same tough questions apply to them, substituting "thread" for 
"process".  And the same main reason for using them -- laziness -- also 
applies.

  In fact, thread-local variables are worse than global variables in at 
least one respect, for exactly the issue you raise here: the same thread 
might well be used for more than one purpose.

  The thing is, this is not a new issue.  People have been using thread 
pools in Java forever.  And have been battling the problems you discuss 
just as long.

  I have encountered those subtle bugs you describe that arise from the 
same thread being reused but its thread-local variables not being 
reinitialized.  They definitely happen.  The case I have in mind was a 
servlet-based web application.  We stored database connections in 
thread-local variables.  Problems arose when some threads needed to set 
different parameters for these connections from others, or needed to 
talk to a different database.  But the solution was not to reinitialize 
everything each time.  The solution was to recognize that there was 
nothing about a database connection that was inherently unique to a 
thread.  As soon as we separated database connections from threads, we 
found that many of the complicated workarounds we had written were 
suddenly superfluous.  For example, establishing two database 
connections had formerly required a second thread; now we simply went to 
our cache a second time within the same thread, an enormous simplification.

  So, I'm equanimous on the subject of whether ThreadLocals should be 
cleared on each use of a thread.  I won't use them regardless.

Luke

Doug Lea wrote:

>Dear concurrency-interest list members,
>
>We in the expert group have been periodically arguing about the
>proposed Thread.removeAllThreadLocals method.  This method erases ALL
>of the ThreadLocals associated with a thread, so that the next time
>access of any of them is attempted, its initialValue() is returned.
>
>The main issues are:
>
> *  Supporting removeAllThreadLocals provides an (optional) way to
>    prevent tasks run by worker Threads in Executors (i.e., mainly,
>    ThreadExecutor) from using "leftover" values of ThreadLocals from
>    previous tasks, which will likely be the source of subtle bugs.
>    Normal use here would be to call removeAllThreadLocals in one or
>    more ExecutorIntercepts methods when you'd like to prevent such
>    problems. This way, each task runs under a worker thread just like
>    it would under a new Thread, except that the thread is actually
>    the same one as used for a previous task.
>
> *  But having such a method is otherwise a potential breach of
>    encapsulation, and could cause problems with other
>    classes/componentss expecting their ThreadLocals to forever be
>    associated with threads, even worker threads that perform many
>    unrelated tasks.
>
>The question is, which is worse, 
>  (1) unexpected reuse, 
>or 
>  (2) unexpected clearing. 
>
>Specifically, is (2) such a serious problem that removeAllThreadLocals
>shouldn't be supported, forcing people to live with (1), or to not use
>ThreadExecutors when this problem arises?
>
>To alleviate (2), if we keep the capacity for removing all
>ThreadLocals, we will probably restructure things with the net effect
>that this can be done only for worker threads within Executors, not
>for arbitrary Threads.
>
>Note that if you know of a particular problematic ThreadLocal, you can
>always either invoke ThreadLocal.set(null) or better, the new
>ThreadLocal.remove() method on it to clear it from within executor
>intercepts. The removeAllThreadLocals method differs here in that it
>also clears those ThreadLocals that the Executor does not otherwise
>know about.
>
>We'd like to collect experiences about this. (Not opinions -- we have
>equally strong "over my dead body" views on each side of this already
>in the expert group!).  If you are currently using some kind of thread
>pool (perhaps dl.u.c.PooledExecutor), and/or programs that make heavy
>use of ThreadLocals, please tell us if:
>
>   * You have encountered problems due to leftover ThreadLocals
>     spilling over across tasks performed by the same thread.
>Or
>
>   * You have code that would break if run via ThreadExecutors that
>     cleared all ThreadLocals between tasks.
>
>Or related horror stories surrounding ThreadLocals.
>
>Thanks very much!
>
>  
>