Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-5025230

(thread) Creating thread local variables from within ThreadLocal.initialValue()

XMLWordPrintable

    • b55
    • generic, x86
    • generic, windows_2000

      Name: rmT116609 Date: 04/01/2004


      A DESCRIPTION OF THE REQUEST :
      Provide a guaranteed method of creating thread local variables from within ThreadLocal.initialValue().

      JUSTIFICATION :
      Currently thread local variables created from within ThreadLocal.initialValue() are not guaranteed to live after the ThreadLocal.get() method completes. That is because get() creates a new ThreadLocalMap possibly overwriting any thread local variables that may have been created from within initialValue().

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
          public Object get()
          {
              Thread t = Thread.currentThread();
              
              ThreadLocalMap map = getMap(t);

              if (map != null)
                  return map.get(this);

              Object value = initialValue();

              ThreadLocalMap map = getMap(t);

              if (map != null)
                  map.set(this, value);
              else
                  createMap(t, value);

              return value;
          }

      ACTUAL -
          public Object get()
          {
              Thread t = Thread.currentThread();
              
              ThreadLocalMap map = getMap(t);

              if (map != null)
                  return map.get(this);

              Object value = initialValue();

              createMap(t, value);

              return value;
          }


      ---------- BEGIN SOURCE ----------
      JUnit / relection uses ThreadLocal I believe, so I had to resort to a POJO test.

      package junit;

      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;

      /**
       * @author Harish Krishnaswamy
       * @version $Id: $
       * @since 1.0
       */
      public class TestThreadLocal
      {
          private static class TestThreadEventNotifier
          {
              private ThreadLocal _local = new ThreadLocal();

              private List getListeners()
              {
                  List list = (List) _local.get();

                  if (list == null)
                  {
                      list = new ArrayList();
                      _local.set(list);
                  }

                  return list;
              }

              public void addListener(TestThreadLocalStorage listener)
              {
                  List list = getListeners();

                  list.add(listener);
              }

              public void fireThreadCleanup()
              {
                  List list = getListeners();

                  for (Iterator itr = list.iterator(); itr.hasNext();)
                  {
                      TestThreadLocalStorage listener = (TestThreadLocalStorage) itr.next();

                      listener.cleanup();
                  }
              }
          }

          private static class TestThreadLocalStorage
          {
              private CleanableThreadLocal _local;

              TestThreadLocalStorage(TestThreadEventNotifier notifier)
              {
                  _local = new CleanableThreadLocal(this, notifier);
              }

              private class CleanableThreadLocal extends ThreadLocal
              {
                  private TestThreadLocalStorage _storage;
                  private TestThreadEventNotifier _notifier;

                  private CleanableThreadLocal(TestThreadLocalStorage storage,
                      TestThreadEventNotifier notifier)
                  {
                      _storage = storage;
                      _notifier = notifier;
                  }

                  protected Object initialValue()
                  {
                      if (_notifier != null && _storage != null)
                          _notifier.addListener(_storage);

                      return new HashMap();
                  }
              }

              public Object get(Object key)
              {
                  Map map = (Map) _local.get();

                  return map.get(key);
              }

              public void put(Object key, Object value)
              {
                  Map map = (Map) _local.get();

                  map.put(key, value);
              }

              public void cleanup()
              {
                  Map map = (Map) _local.get();

                  map.clear();
              }

          }

          public static void main(String[] args)
          {
              TestThreadEventNotifier notifier = new TestThreadEventNotifier();
              TestThreadLocalStorage storage = new TestThreadLocalStorage(notifier);

              storage.put("mykey", "myvalue");

              notifier.fireThreadCleanup();

              Object myVal = storage.get("mykey");
              
              if (myVal == null)
                  System.out.println("ThreadLocal values successfully cleared!");
              else
                  System.out.println("ERROR: ThreadLocal values not cleared: " + myVal);
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      package junit;

      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;

      /**
       * @author Harish Krishnaswamy
       * @version $Id: $
       * @since 1.0
       */
      public class TestThreadLocalWorkaround
      {
          private static class TestThreadEventNotifier
          {
              private ThreadLocal _local = new ThreadLocal();

              private List getListeners()
              {
                  List list = (List) _local.get();

                  if (list == null)
                  {
                      list = new ArrayList();
                      _local.set(list);
                  }

                  return list;
              }

              public void addListener(ThreadLocalStorage listener)
              {
                  List list = getListeners();

                  list.add(listener);
              }

              public void fireThreadCleanup()
              {
                  List list = getListeners();

                  for (Iterator itr = list.iterator(); itr.hasNext();)
                  {
                      ThreadLocalStorage listener = (ThreadLocalStorage) itr.next();

                      listener.cleanup();
                  }
              }
          }

          private static class ThreadLocalStorage
          {
              private static final String INITIALIZED_KEY = "$init$";

              private CleanableThreadLocal _local = new CleanableThreadLocal();
              private TestThreadEventNotifier _notifier;

              ThreadLocalStorage(TestThreadEventNotifier notifier)
              {
                  _notifier = notifier;
              }

              private class CleanableThreadLocal extends ThreadLocal
              {
                  protected Object initialValue()
                  {
                      Map map = new HashMap();
                      map.put(INITIALIZED_KEY, Boolean.TRUE);

                      return map;
                  }
              }

              private Map getThreadLocalVariable()
              {
                  Map map = (Map) _local.get();

                  if (Boolean.TRUE.equals(map.get(INITIALIZED_KEY)) && _notifier != null)
                  {
                      _notifier.addListener(this);

                      map.remove(INITIALIZED_KEY);
                  }

                  return map;
              }

              public Object get(Object key)
              {
                  Map map = getThreadLocalVariable();

                  return map.get(key);
              }

              public void put(Object key, Object value)
              {
                  Map map = getThreadLocalVariable();

                  map.put(key, value);
              }

              public void cleanup()
              {
                  Map map = (Map) _local.get();

                  map.clear();
              }

          }

          public static void main(String[] args)
          {
              TestThreadEventNotifier notifier = new TestThreadEventNotifier();
              ThreadLocalStorage storage = new ThreadLocalStorage(notifier);

              storage.put("mykey", "myvalue");

              notifier.fireThreadCleanup();

              Object myVal = storage.get("mykey");
              
              if (myVal == null)
                  System.out.println("ThreadLocal values successfully cleared!");
              else
                  System.out.println("ERROR: ThreadLocal values not cleared: " + myVal);
          }
      }
      (Incident Review ID: 241038)
      ======================================================================

            psoper Pete Soper (Inactive)
            rmandalasunw Ranjith Mandala (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: