如何测试ThreadLocal是否已初始化而没有实际执行此操作?

时间:2012-07-14 13:21:31

标签: java multithreading thread-safety

我想测试一个ThreadLocal是否已经初始化而没有实际初始化它。当然,代码需要是线程安全的。理想情况下,我想要这样的事情:

class TestableThreadLocal<T> extends ThreadLocal<T> {
    public boolean isInitialized() {
        ...
    }
}

但是我该如何实现这种方法?

编辑:动机:我已将ThreadLocal子类化为覆盖initialValue()。但是,我并不总是需要初始化,特别是因为它可能会导致多类加载器环境中的内存泄漏。一个简单的测试可以帮助我编写代码以避免意外初始化。

4 个答案:

答案 0 :(得分:11)

我倾向于使用简单的规则来避免类加载器泄漏,不可否认它们很麻烦,但有一些包装并没有那么糟糕。是的,泄漏是邪恶的。

  • 永远不要覆盖initialValue - 你只是在寻找麻烦,只是忘记它存在。
  • 除非您执行threadLocal.set(xxx); try{...process...}finally{threadLocal.set(null);}
  • ,否则不要在ThreadLocal中存储非系统/非bootstap类
  • 如果您仍覆盖initialValue,请使用threadLocal.remove()而不是threadLocal.set(null)
  • 或使用WeakReferene(即ThreadLocal <WeakReference<Foo>>)获取保存硬引用的池中的值。它可能看起来很直观,但一旦清除了池,值就会消失,类可以自由地进行GC

我意识到这篇文章不是对你的问题的直接回复,但是没有简单的方法可以达到你想要的目的并防止泄密。

答案 1 :(得分:2)

思想:

  • 如果调用ThreadLocal.get,则初始化值。
  • 如果您不想初始化它,则不需要存储的值(根本不存在)
  • 如果您想避免初始化,似乎可以通过覆盖get,而不是initialValue来启动。
  • 采取不同的方式 - 你真的需要继承ThreadLocal而不是遏制吗?

答案 2 :(得分:1)

执行此操作的几种可能方法之一将涉及反射,因为ThreadLocal没有可让您知道该值是否已初始化的API。

使用反射“编码”它当然是可能的。但是,所有常见的警告都适用。您的反射代码将高度依赖于java.lang.ThreadLocal及其非公共成员的实现细节。 JDK供应商改变实施的那一刻,你的代码就会崩溃。

答案 3 :(得分:1)

我对你最初的问题感到困惑。您正在创建的ThreadLocal是否已由其他人提供?

如果您正在创建ThreadLocal,那么我假设您已经知道必须覆盖initialValue(),以便您可以提供初始值。

在这种情况下,您始终可以跟踪是否已调用该值并已创建该值。

我认为下面这段代码会做你想做的事情:

MyThreadLocal<T> extends ThreadLocal<T>{

   Map<Thread,Boolean> myThreadMap = new HashMap<Thread,Boolean>();

   protected T initialValue(){
         synchronized(myThreadMap){
               T value =  ... your code to create initial value
               myThreadMap.put(Thread.currentThread(),true);
               return T;
         }
   }

   public boolean containsThreadValues(){
          synchronized(myThreadMap){
                 return myThreadMap.isEmpty();
          }
   }

   public boolean isInitializedForThisThread(){
         synchronized(myThreadMap){
                 return myThreadMap.get(Thread.currentThread())==true;
         }
   }


}

myThreadMap将始终包含旧密钥,即使对于死线程也是如此。如果要清除旧密钥,则将其替换为WeakHashMap。然后,当使用ThreadLocal的线程消失时,键将被删除。