[concurrency-interest] NullPointerExceptionin ThreadLocal$ThreadLocalMap.replaceStaleEntry

Thomas Hawtin tackline at tackline.plus.com
Fri Jun 30 10:58:10 EDT 2006


Doug Lea wrote:
> 
> As Joe just mentioned, there was an unsupported usage (that
> was not clearly documented as unsupported) of initialValue
> methods recursively creating other ThreadLocals, that was
> addressed for Mustang, and which might conceivably cause this.
> It might not be too hard to look in your code to see if
> there are any cases of this. Otherwise, offhand, this looks
> like it might be a GC bug, but no one will be able to figure
> it out unless they can replicate.

Looking through the source, it does appear to be Bug 5025230 [1] as Joe 
says. However, this case is a bit more subtle than the scenario in the 
report. It appears that the nested thread-local initialisation causes 
some tidying of stale entries, which clears the slot about to be filled 
(probably on a complete rehash of the thread's table).

Anyway, the result is the same. Initialising ThreadLocals within 
initialValue doesn't work in both 1.4.2_11 and 1.5.0_07. Currently you 
need mustang for it to work (or patch your own JRE - I believe there is 
a license to do this for internal use (IANAL)).

Avoiding nested initialisation may be more difficult than it would first 
appear. As well as your own code in initialValue, you need to avoid 
using anyone else's code that might initiliase ThreadLocals. For 
instance, loading classes can cause ThreadLocals to initialise (that 
confused me briefly).

You could try flushing out any ThreadLocals in every thread before using 
initialValue. However, the obvious safe approach is to override get in 
place of initialValue. Something like:

public abstract class SaferThreadLocal/*<T>*/
extends ThreadLocal/*<T>*/
{
     private static final Object UNINITIALIZED = new Object();

     //@Override
     protected final Object/*T*/ initialValue() {
         return UNINITIALIZED;
     }

     protected abstract Object/*T*/ initialValueOverride();

     //@SuppressWarnings("unchecked")
     //@Override
     protected final Object/*T*/ get() {
         Object obj = super.get();
         if (obj == UNINITIALIZED) {
             obj = initialValueOverride();
             super.set(obj);
         }
         return /*(T)*/obj;
     }
}

(Disclaimer: code not tested, or even compiled.)

Tom Hawtin

[1] http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5025230



More information about the Concurrency-interest mailing list